Merge "Regenerate API signatures to add missing nullability annotations"
diff --git a/Android.bp b/Android.bp
index 90dca03..8a21a9c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -34,6 +34,27 @@
path: "core/java",
}
+// These are subset of framework-core-sources that are needed by the
+// android.test.mock library. Ideally, the library should use public APIs only,
+// but unfortunately its API signature has some references to these private APIs.
+filegroup {
+ name: "framework-core-sources-for-test-mock",
+ srcs: [
+ "core/java/android/app/IApplicationThread.aidl",
+ "core/java/android/app/IServiceConnection.aidl",
+ "core/java/android/content/IContentProvider.java",
+ "core/java/android/content/pm/IPackageDataObserver.aidl",
+ "core/java/android/content/pm/InstantAppInfo.java",
+ "core/java/android/content/pm/KeySet.java",
+ "core/java/android/content/pm/PackageManager.java",
+ "core/java/android/content/pm/VerifierDeviceIdentity.java",
+ "core/java/android/content/res/Resources.java",
+ "core/java/android/os/storage/VolumeInfo.java",
+ "core/java/android/view/DisplayAdjustments.java",
+ ],
+ path: "core/java",
+}
+
filegroup {
name: "framework-drm-sources",
srcs: [
@@ -162,6 +183,14 @@
}
filegroup {
+ name: "framework-telephony-common-sources",
+ srcs: [
+ "telephony/common/**/*.java",
+ ],
+ path: "telephony/common",
+}
+
+filegroup {
name: "framework-wifi-sources",
srcs: [
"wifi/java/**/*.java",
@@ -189,6 +218,7 @@
":framework-rs-sources",
":framework-sax-sources",
":framework-telecomm-sources",
+ ":framework-telephony-common-sources",
":framework-telephony-sources",
":framework-wifi-sources",
":PacProcessor-aidl-sources",
@@ -833,6 +863,7 @@
"opengl/java",
"sax/java",
"telecomm/java",
+ "telephony/common",
"telephony/java",
"wifi/java",
"lowpan/java",
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6a909c0..f7a2858 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -254,6 +254,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DefaultContainerService)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CaptivePortalLogin)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/ext.jar)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/google/android/mms)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/api/current.txt b/api/current.txt
index e4300f2..0adb5d5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29366,7 +29366,8 @@
}
public class AudioGroup {
- ctor public AudioGroup();
+ ctor @Deprecated public AudioGroup();
+ ctor public AudioGroup(@Nullable android.content.Context);
method public void clear();
method public int getMode();
method public android.net.rtp.AudioStream[] getStreams();
@@ -44439,6 +44440,7 @@
method public int describeContents();
method public int getAsuLevel();
method public int getDbm();
+ method public int getEcNo();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
@@ -45021,6 +45023,7 @@
method @Nullable public CharSequence getSimSpecificCarrierIdName();
method public int getSimState();
method public int getSimState(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getSubIdForPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSubscriberId();
method public int getSupportedModemCount();
method @Nullable public String getTypeAllocationCode();
diff --git a/api/system-current.txt b/api/system-current.txt
index 5c576e2..6134b94 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7246,9 +7246,9 @@
public abstract class CellBroadcastService extends android.app.Service {
ctor public CellBroadcastService();
- method @CallSuper public android.os.IBinder onBind(android.content.Intent);
- method public abstract void onCdmaCellBroadcastSms(int, byte[]);
- method public abstract void onGsmCellBroadcastSms(int, byte[]);
+ method @CallSuper @NonNull public android.os.IBinder onBind(@Nullable android.content.Intent);
+ method public abstract void onCdmaCellBroadcastSms(int, @NonNull byte[]);
+ method public abstract void onGsmCellBroadcastSms(int, @NonNull byte[]);
field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
}
@@ -8201,6 +8201,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
method public boolean isDataConnectivityPossible();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
@@ -8236,6 +8237,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
method public void updateServiceLocation();
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_OTA_EMERGENCY_NUMBER_DB_INSTALLED = "android.telephony.action.OTA_EMERGENCY_NUMBER_DB_INSTALLED";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
@@ -8812,9 +8814,11 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
- public class ImsMmTelManager {
+ public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled();
@@ -8825,7 +8829,8 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
@@ -8835,7 +8840,8 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
@@ -8847,12 +8853,8 @@
method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
}
- public static class ImsMmTelManager.RegistrationCallback {
- ctor public ImsMmTelManager.RegistrationCallback();
- method public void onRegistered(int);
- method public void onRegistering(int);
- method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
- method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+ @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
+ ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
}
public final class ImsReasonInfo implements android.os.Parcelable {
@@ -9284,6 +9286,24 @@
method public void onProvisioningStringChanged(int, @NonNull String);
}
+ public interface RegistrationManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
+ field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
+ field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
+ }
+
+ public static class RegistrationManager.RegistrationCallback {
+ ctor public RegistrationManager.RegistrationCallback();
+ method public void onRegistered(int);
+ method public void onRegistering(int);
+ method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+ }
+
}
package android.telephony.ims.feature {
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 3e00bfe..2611e96 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1468,283 +1468,4 @@
Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
Lcom/google/android/gles_jni/EGLImpl;-><init>()V
Lcom/google/android/gles_jni/GLImpl;-><init>()V
-Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/InvalidHeaderValueException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>()V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/Base64;->decodeBase64([B)[B
-Lcom/google/android/mms/pdu/CharacterSets;->getMibEnumValue(Ljava/lang/String;)I
-Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
-Lcom/google/android/mms/pdu/DeliveryInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/DeliveryInd;->getDate()J
-Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/DeliveryInd;->getStatus()I
-Lcom/google/android/mms/pdu/DeliveryInd;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->appendTextString([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->copy(Lcom/google/android/mms/pdu/EncodedStringValue;)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->extract(Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getCharacterSet()I
-Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getTextString()[B
-Lcom/google/android/mms/pdu/EncodedStringValue;->setCharacterSet(I)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->setTextString([B)V
-Lcom/google/android/mms/pdu/GenericPdu;-><init>()V
-Lcom/google/android/mms/pdu/GenericPdu;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
-Lcom/google/android/mms/pdu/GenericPdu;->getPduHeaders()Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->mPduHeaders:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/GenericPdu;->setMessageType(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>()V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->addTo(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>()V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotificationInd;->getContentClass()I
-Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
-Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
-Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
-Lcom/google/android/mms/pdu/NotificationInd;->setContentClass(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setExpiry(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setStatus(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/PduBody;-><init>()V
-Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
-Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
-Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartIndex(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
-Lcom/google/android/mms/pdu/PduBody;->removePart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->copy()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->mark()Lcom/google/android/mms/pdu/PduComposer$PositionMarker;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->newbuf()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->pop()V
-Lcom/google/android/mms/pdu/PduComposer$PositionMarker;->getLength()I
-Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendEncodedString(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendHeader(I)I
-Lcom/google/android/mms/pdu/PduComposer;->appendLongInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendOctet(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendShortInteger(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendUintvarInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendValueLength(J)V
-Lcom/google/android/mms/pdu/PduComposer;->arraycopy([BII)V
-Lcom/google/android/mms/pdu/PduComposer;->make()[B
-Lcom/google/android/mms/pdu/PduComposer;->mContentTypeMap:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduComposer;->mMessage:Ljava/io/ByteArrayOutputStream;
-Lcom/google/android/mms/pdu/PduComposer;->mPdu:Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduComposer;->mPduHeader:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/PduComposer;->mPosition:I
-Lcom/google/android/mms/pdu/PduComposer;->mResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduComposer;->mStack:Lcom/google/android/mms/pdu/PduComposer$BufferStack;
-Lcom/google/android/mms/pdu/PduContentTypes;->contentTypes:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduHeaders;-><init>()V
-Lcom/google/android/mms/pdu/PduHeaders;->appendEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValue(I)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValues(I)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getLongInteger(I)J
-Lcom/google/android/mms/pdu/PduHeaders;->getOctet(I)I
-Lcom/google/android/mms/pdu/PduHeaders;->getTextString(I)[B
-Lcom/google/android/mms/pdu/PduHeaders;->setEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->setLongInteger(JI)V
-Lcom/google/android/mms/pdu/PduHeaders;->setOctet(II)V
-Lcom/google/android/mms/pdu/PduParser;->$assertionsDisabled:Z
-Lcom/google/android/mms/pdu/PduParser;-><init>([BZ)V
-Lcom/google/android/mms/pdu/PduParser;->checkPartPosition(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduParser;->log(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduParser;->parseContentType(Ljava/io/ByteArrayInputStream;Ljava/util/HashMap;)[B
-Lcom/google/android/mms/pdu/PduParser;->parsePartHeaders(Ljava/io/ByteArrayInputStream;Lcom/google/android/mms/pdu/PduPart;I)Z
-Lcom/google/android/mms/pdu/PduParser;->parseShortInteger(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseUnsignedInt(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseValueLength(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseWapString(Ljava/io/ByteArrayInputStream;I)[B
-Lcom/google/android/mms/pdu/PduPart;-><init>()V
-Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPart;->getCharset()I
-Lcom/google/android/mms/pdu/PduPart;->getContentDisposition()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentId()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentTransferEncoding()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
-Lcom/google/android/mms/pdu/PduPart;->getData()[B
-Lcom/google/android/mms/pdu/PduPart;->getDataLength()I
-Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
-Lcom/google/android/mms/pdu/PduPart;->getName()[B
-Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
-Lcom/google/android/mms/pdu/PduPart;->setContentDisposition([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentTransferEncoding([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
-Lcom/google/android/mms/pdu/PduPart;->setData([B)V
-Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
-Lcom/google/android/mms/pdu/PduPart;->setFilename([B)V
-Lcom/google/android/mms/pdu/PduPart;->setName([B)V
-Lcom/google/android/mms/pdu/PduPersister;->ADDRESS_FIELDS:[I
-Lcom/google/android/mms/pdu/PduPersister;->CHARSET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->ENCODED_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->getByteArrayFromPartColumn(Landroid/database/Cursor;I)[B
-Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
-Lcom/google/android/mms/pdu/PduPersister;->getIntegerFromPartColumn(Landroid/database/Cursor;I)Ljava/lang/Integer;
-Lcom/google/android/mms/pdu/PduPersister;->getPartContentType(Lcom/google/android/mms/pdu/PduPart;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
-Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
-Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduPersister;->loadRecipients(ILjava/util/HashSet;Ljava/util/HashMap;Z)V
-Lcom/google/android/mms/pdu/PduPersister;->LONG_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->mContentResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduPersister;->mContext:Landroid/content/Context;
-Lcom/google/android/mms/pdu/PduPersister;->MESSAGE_BOX_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->mTelephonyManager:Landroid/telephony/TelephonyManager;
-Lcom/google/android/mms/pdu/PduPersister;->OCTET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->PART_PROJECTION:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->PDU_CACHE_INSTANCE:Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->persistAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->TEXT_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->updateAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
-Lcom/google/android/mms/pdu/QuotedPrintable;->decodeQuotedPrintable([B)[B
-Lcom/google/android/mms/pdu/ReadOrigInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadOrigInd;->getReadStatus()I
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadRecInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>()V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getContentType()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageClass()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getReadReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveStatus()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveText()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->setContentType([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageId([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setReadReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveStatus(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveText(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/SendConf;-><init>()V
-Lcom/google/android/mms/pdu/SendConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
-Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;-><init>()V
-Lcom/google/android/mms/pdu/SendReq;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/SendReq;->addBcc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getContentType()[B
-Lcom/google/android/mms/pdu/SendReq;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/SendReq;->getExpiry()J
-Lcom/google/android/mms/pdu/SendReq;->getMessageClass()[B
-Lcom/google/android/mms/pdu/SendReq;->getMessageSize()J
-Lcom/google/android/mms/pdu/SendReq;->getReadReport()I
-Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;->setBcc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setCc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setContentType([B)V
-Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setTransactionId([B)V
-Lcom/google/android/mms/util/AbstractCache;-><init>()V
-Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purgeAll()V
-Lcom/google/android/mms/util/AbstractCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->isDrmConvertNeeded(Ljava/lang/String;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->modifyDrmFwLockFileExtension(Ljava/lang/String;)Ljava/lang/String;
-Lcom/google/android/mms/util/DrmConvertSession;->close(Ljava/lang/String;)I
-Lcom/google/android/mms/util/DrmConvertSession;->convert([BI)[B
-Lcom/google/android/mms/util/DrmConvertSession;->open(Landroid/content/Context;Ljava/lang/String;)Lcom/google/android/mms/util/DrmConvertSession;
-Lcom/google/android/mms/util/PduCache;-><init>()V
-Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
-Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
-Lcom/google/android/mms/util/PduCache;->purgeAll()V
-Lcom/google/android/mms/util/PduCacheEntry;-><init>(Lcom/google/android/mms/pdu/GenericPdu;IJ)V
-Lcom/google/android/mms/util/PduCacheEntry;->getMessageBox()I
-Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/util/PduCacheEntry;->getThreadId()J
-Lcom/google/android/mms/util/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
-Lcom/google/android/mms/util/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
-Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Lcom/google/android/mms/util/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
-Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
-Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9aacbe6..1f20d7a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -497,6 +497,7 @@
<protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
<protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
<protected-broadcast android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.OTA_EMERGENCY_NUMBER_DB_INSTALLED" />
<protected-broadcast android:name="android.telephony.action.SECRET_CODE" />
<protected-broadcast android:name="android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION" />
<protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_PLANS_CHANGED" />
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0460a80..461743e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9334,6 +9334,16 @@
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName;
+ if (scanSystemPartition && !pkgAlreadyExists
+ && mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
+ // The updated-package data for /system apk remains inconsistently
+ // after the package data for /data apk is lost accidentally.
+ // To recover it, enable /system apk and install it as non-updated system app.
+ Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ + disabledPkgName + ". To recover it, enable the system app"
+ + "and install it as non-updated system app.");
+ mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
+ }
disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
diff --git a/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java b/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java
index 852f707..cb0b45c 100644
--- a/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.Intent;
+import android.telephony.TelephonyManager;
import android.util.Slog;
/**
@@ -34,6 +35,11 @@
@Override
protected void postInstall(Context context, Intent intent) {
Slog.i(TAG, "Emergency number database is updated in file partition");
- // TODO Send a notification to EmergencyNumberTracker for updating of emergency number db.
+
+ // Notify EmergencyNumberTracker for emergency number installation complete.
+ Intent notifyInstallComplete = new Intent(
+ TelephonyManager.ACTION_OTA_EMERGENCY_NUMBER_DB_INSTALLED);
+ context.sendBroadcast(
+ notifyInstallComplete, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 248baf7..d6fbd35 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -136,6 +136,7 @@
"android.frameworks.sensorservice@1.0",
"android.system.suspend@1.0",
"suspend_control_aidl_interface-cpp",
+ "vintf-vibrator-cpp",
],
static_libs: [
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 64c7935..8ddb86b 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -17,6 +17,9 @@
#define LOG_TAG "VibratorService"
#include <android/hardware/vibrator/1.4/IVibrator.h>
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
#include "jni.h"
#include <nativehelper/JNIHelp.h>
@@ -41,11 +44,147 @@
namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
namespace V1_4 = android::hardware::vibrator::V1_4;
+namespace aidl = android::hardware::vibrator;
namespace android {
static jmethodID sMethodIdOnComplete;
+// TODO(b/141828236): remove HIDL 1.4 and re-write all of this code to remove
+// shim
+class VibratorShim : public V1_4::IVibrator {
+ public:
+ VibratorShim(const sp<aidl::IVibrator>& vib) : mVib(vib) {}
+
+ Return<V1_0::Status> on(uint32_t timeoutMs) override {
+ return on_1_4(timeoutMs, nullptr);
+ }
+
+ Return<V1_0::Status> off() override {
+ return toHidlStatus(mVib->off());
+ }
+
+ Return<bool> supportsAmplitudeControl() override {
+ int32_t cap = 0;
+ if (!mVib->getCapabilities(&cap).isOk()) return false;
+ return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0;
+ }
+
+ Return<V1_0::Status> setAmplitude(uint8_t amplitude) override {
+ return toHidlStatus(mVib->setAmplitude(amplitude));
+ }
+
+ Return<void> perform(V1_0::Effect effect, V1_0::EffectStrength strength,
+ perform_cb _hidl_cb) override {
+ return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb);
+ }
+
+ Return<void> perform_1_1(V1_1::Effect_1_1 effect, V1_0::EffectStrength strength,
+ perform_1_1_cb _hidl_cb) override {
+ return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb);
+ }
+
+ Return<void> perform_1_2(V1_2::Effect effect, V1_0::EffectStrength strength,
+ perform_1_2_cb _hidl_cb) override {
+ return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb);
+ }
+
+ Return<bool> supportsExternalControl() override {
+ int32_t cap = 0;
+ if (!mVib->getCapabilities(&cap).isOk()) return false;
+ return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0;
+ }
+
+ Return<V1_0::Status> setExternalControl(bool enabled) override {
+ return toHidlStatus(mVib->setExternalControl(enabled));
+ }
+
+ Return<void> perform_1_3(V1_3::Effect effect, V1_0::EffectStrength strength,
+ perform_1_3_cb _hidl_cb) override {
+ return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb);
+ }
+
+ Return<uint32_t> getCapabilities() override {
+ static_assert(static_cast<int32_t>(V1_4::Capabilities::ON_COMPLETION_CALLBACK) ==
+ static_cast<int32_t>(aidl::IVibrator::CAP_ON_CALLBACK));
+ static_assert(static_cast<int32_t>(V1_4::Capabilities::PERFORM_COMPLETION_CALLBACK) ==
+ static_cast<int32_t>(aidl::IVibrator::CAP_PERFORM_CALLBACK));
+
+ int32_t cap;
+ if (!mVib->getCapabilities(&cap).isOk()) return 0;
+ return (cap & (aidl::IVibrator::CAP_ON_CALLBACK |
+ aidl::IVibrator::CAP_PERFORM_CALLBACK)) > 0;
+ }
+
+ Return<V1_0::Status> on_1_4(uint32_t timeoutMs,
+ const sp<V1_4::IVibratorCallback>& callback) override {
+ sp<aidl::IVibratorCallback> cb = callback ? new CallbackShim(callback) : nullptr;
+ return toHidlStatus(mVib->on(timeoutMs, cb));
+ }
+
+ Return<void> perform_1_4(V1_3::Effect effect, V1_0::EffectStrength strength,
+ const sp<V1_4::IVibratorCallback>& callback,
+ perform_1_4_cb _hidl_cb) override {
+ static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
+ static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
+ static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
+ static_cast<uint8_t>(aidl::EffectStrength::MEDIUM));
+ static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
+ static_cast<uint8_t>(aidl::EffectStrength::STRONG));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
+ static_cast<uint8_t>(aidl::Effect::CLICK));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
+ static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) ==
+ static_cast<uint8_t>(aidl::Effect::TICK));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) ==
+ static_cast<uint8_t>(aidl::Effect::THUD));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::POP) ==
+ static_cast<uint8_t>(aidl::Effect::POP));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
+ static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
+ static_cast<uint8_t>(aidl::Effect::RINGTONE_1));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
+ static_cast<uint8_t>(aidl::Effect::RINGTONE_2));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
+ static_cast<uint8_t>(aidl::Effect::RINGTONE_15));
+ static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
+ static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
+
+ sp<aidl::IVibratorCallback> cb = callback ? new CallbackShim(callback) : nullptr;
+ int timeoutMs = 0;
+ V1_0::Status status = toHidlStatus(
+ mVib->perform(static_cast<aidl::Effect>(effect),
+ static_cast<aidl::EffectStrength>(strength), cb, &timeoutMs));
+ _hidl_cb(status, timeoutMs);
+ return android::hardware::Status::ok();
+ }
+ private:
+ sp<aidl::IVibrator> mVib;
+
+ V1_0::Status toHidlStatus(const android::binder::Status& status) {
+ switch(status.exceptionCode()) {
+ using android::hardware::Status;
+ case Status::EX_NONE: return V1_0::Status::OK;
+ case Status::EX_ILLEGAL_ARGUMENT: return V1_0::Status::BAD_VALUE;
+ case Status::EX_UNSUPPORTED_OPERATION: return V1_0::Status::UNSUPPORTED_OPERATION;
+ }
+ return V1_0::Status::UNKNOWN_ERROR;
+ }
+
+ class CallbackShim : public aidl::BnVibratorCallback {
+ public:
+ CallbackShim(const sp<V1_4::IVibratorCallback>& cb) : mCb(cb) {}
+ binder::Status onComplete() {
+ mCb->onComplete();
+ return binder::Status::ok(); // oneway, local call
+ }
+ private:
+ sp<V1_4::IVibratorCallback> mCb;
+ };
+};
+
class VibratorCallback : public V1_4::IVibratorCallback {
public:
VibratorCallback(JNIEnv *env, jobject vibration) :
@@ -79,6 +218,11 @@
class HalWrapper {
public:
static std::unique_ptr<HalWrapper> Create() {
+ sp<aidl::IVibrator> aidlVib = waitForVintfService<aidl::IVibrator>();
+ if (aidlVib) {
+ return std::unique_ptr<HalWrapper>(new HalWrapper(new VibratorShim(aidlVib)));
+ }
+
// Assume that if getService returns a nullptr, HAL is not available on the
// device.
auto hal = I::getService();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f465855..16c60ca 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.app.ActivityThread;
+import android.app.AppCompatCallbacks;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -644,6 +645,7 @@
ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
new PlatformCompatNative(platformCompat));
+ AppCompatCallbacks.install(new long[0]);
traceEnd();
// Wait for installd to finish starting up so that it has a chance to
diff --git a/telephony/common/com/android/internal/telephony/HbpcdLookup.java b/telephony/common/com/android/internal/telephony/HbpcdLookup.java
new file mode 100644
index 0000000..d9a3e72
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/HbpcdLookup.java
@@ -0,0 +1,124 @@
+/*
+**
+** Copyright 2014, 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.internal.telephony;
+
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+/**
+ * @hide
+ */
+public class HbpcdLookup {
+ public static final String AUTHORITY = "hbpcd_lookup";
+
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY);
+
+ public static final String PATH_MCC_IDD = "idd";
+ public static final String PATH_MCC_LOOKUP_TABLE = "lookup";
+ public static final String PATH_MCC_SID_CONFLICT = "conflict";
+ public static final String PATH_MCC_SID_RANGE = "range";
+ public static final String PATH_NANP_AREA_CODE = "nanp";
+ public static final String PATH_ARBITRARY_MCC_SID_MATCH = "arbitrary";
+ public static final String PATH_USERADD_COUNTRY = "useradd";
+
+ public static final String ID = "_id";
+ public static final int IDINDEX = 0;
+
+ /**
+ * @hide
+ */
+ public static class MccIdd implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_IDD);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String IDD = "IDD";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccLookup implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_LOOKUP_TABLE);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String COUNTRY_CODE = "Country_Code";
+ public static final String COUNTRY_NAME = "Country_Name";
+ public static final String NDD = "NDD";
+ public static final String NANPS = "NANPS";
+ public static final String GMT_OFFSET_LOW = "GMT_Offset_Low";
+ public static final String GMT_OFFSET_HIGH = "GMT_Offset_High";
+ public static final String GMT_DST_LOW = "GMT_DST_Low";
+ public static final String GMT_DST_HIGH = "GMT_DST_High";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccSidConflicts implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_CONFLICT);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String SID_CONFLICT = "SID_Conflict";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccSidRange implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_RANGE);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String RANGE_LOW = "SID_Range_Low";
+ public static final String RANGE_HIGH = "SID_Range_High";
+ }
+
+ /**
+ * @hide
+ */
+ public static class ArbitraryMccSidMatch implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_ARBITRARY_MCC_SID_MATCH);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String SID = "SID";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class NanpAreaCode implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_NANP_AREA_CODE);
+ public static final String DEFAULT_SORT_ORDER = "Area_Code ASC";
+
+ public static final String AREA_CODE = "Area_Code";
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/HbpcdUtils.java b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
new file mode 100644
index 0000000..2f31942
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 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.internal.telephony;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
+import com.android.internal.telephony.HbpcdLookup.MccIdd;
+import com.android.internal.telephony.HbpcdLookup.MccLookup;
+import com.android.internal.telephony.HbpcdLookup.MccSidConflicts;
+import com.android.internal.telephony.HbpcdLookup.MccSidRange;
+
+public final class HbpcdUtils {
+ private static final String LOG_TAG = "HbpcdUtils";
+ private static final boolean DBG = false;
+ private ContentResolver resolver = null;
+
+ public HbpcdUtils(Context context) {
+ resolver = context.getContentResolver();
+ }
+
+ /**
+ * Resolves the unknown MCC with SID and Timezone information.
+ */
+ public int getMcc(int sid, int tz, int DSTflag, boolean isNitzTimeZone) {
+ int tmpMcc = 0;
+
+ // check if SID exists in arbitrary_mcc_sid_match table.
+ // these SIDs are assigned to more than 1 operators, but they are known to
+ // be used by a specific operator, other operators having the same SID are
+ // not using it currently, if that SID is in this table, we don't need to
+ // check other tables.
+ String projection2[] = {ArbitraryMccSidMatch.MCC};
+ Cursor c2 = resolver.query(ArbitraryMccSidMatch.CONTENT_URI, projection2,
+ ArbitraryMccSidMatch.SID + "=" + sid, null, null);
+
+ if (c2 != null) {
+ int c2Counter = c2.getCount();
+ if (DBG) {
+ Rlog.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
+ }
+ if (c2Counter == 1) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2);
+ }
+ c2.moveToFirst();
+ tmpMcc = c2.getInt(0);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
+ }
+ c2.close();
+ return tmpMcc;
+ }
+ c2.close();
+ }
+
+ // Then check if SID exists in mcc_sid_conflict table.
+ // and use the timezone in mcc_lookup table to check which MCC matches.
+ String projection3[] = {MccSidConflicts.MCC};
+ Cursor c3 = resolver.query(MccSidConflicts.CONTENT_URI, projection3,
+ MccSidConflicts.SID_CONFLICT + "=" + sid + " and (((" +
+ MccLookup.GMT_OFFSET_LOW + "<=" + tz + ") and (" + tz + "<=" +
+ MccLookup.GMT_OFFSET_HIGH + ") and (" + "0=" + DSTflag + ")) or ((" +
+ MccLookup.GMT_DST_LOW + "<=" + tz + ") and (" + tz + "<=" +
+ MccLookup.GMT_DST_HIGH + ") and (" + "1=" + DSTflag + ")))",
+ null, null);
+ if (c3 != null) {
+ int c3Counter = c3.getCount();
+ if (c3Counter > 0) {
+ if (c3Counter > 1) {
+ Rlog.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
+ }
+ if (DBG) Rlog.d(LOG_TAG, "Query conflict sid returned the cursor " + c3);
+ c3.moveToFirst();
+ tmpMcc = c3.getInt(0);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
+ }
+ if (!isNitzTimeZone) {
+ // time zone is not accurate, it may get wrong mcc, ignore it.
+ if (DBG) {
+ Rlog.d(LOG_TAG, "time zone is not accurate, mcc may be " + tmpMcc);
+ }
+ tmpMcc = 0;
+ }
+ c3.close();
+ return tmpMcc;
+ } else {
+ c3.close();
+ }
+ }
+
+ // if there is no conflict, then check if SID is in mcc_sid_range.
+ String projection5[] = {MccSidRange.MCC};
+ Cursor c5 = resolver.query(MccSidRange.CONTENT_URI, projection5,
+ MccSidRange.RANGE_LOW + "<=" + sid + " and " +
+ MccSidRange.RANGE_HIGH + ">=" + sid,
+ null, null);
+ if (c5 != null) {
+ if (c5.getCount() > 0) {
+ if (DBG) Rlog.d(LOG_TAG, "Query Range returned the cursor " + c5);
+ c5.moveToFirst();
+ tmpMcc = c5.getInt(0);
+ if (DBG) Rlog.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
+ c5.close();
+ return tmpMcc;
+ }
+ c5.close();
+ }
+ if (DBG) Rlog.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
+
+ if (DBG) Rlog.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc = " + tmpMcc);
+ // If unknown MCC still could not be resolved,
+ return tmpMcc;
+ }
+
+ /**
+ * Gets country information with given MCC.
+ */
+ public String getIddByMcc(int mcc) {
+ if (DBG) Rlog.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
+ String idd = "";
+
+ Cursor c = null;
+
+ String projection[] = {MccIdd.IDD};
+ Cursor cur = resolver.query(MccIdd.CONTENT_URI, projection,
+ MccIdd.MCC + "=" + mcc, null, null);
+ if (cur != null) {
+ if (cur.getCount() > 0) {
+ if (DBG) Rlog.d(LOG_TAG, "Query Idd returned the cursor " + cur);
+ // TODO: for those country having more than 1 IDDs, need more information
+ // to decide which IDD would be used. currently just use the first 1.
+ cur.moveToFirst();
+ idd = cur.getString(0);
+ if (DBG) Rlog.d(LOG_TAG, "IDD = " + idd);
+
+ }
+ cur.close();
+ }
+ if (c != null) c.close();
+
+ if (DBG) Rlog.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
+ return idd;
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
new file mode 100644
index 0000000..0d33af6
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2014 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.internal.telephony;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.os.Binder;
+import android.os.Build;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.HbpcdLookup.MccIdd;
+import com.android.internal.telephony.HbpcdLookup.MccLookup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ * This class implements handle the MO SMS target address before sending.
+ * This is special for VZW requirement. Follow the specifications of assisted dialing
+ * of MO SMS while traveling on VZW CDMA, international CDMA or GSM markets.
+ * {@hide}
+ */
+public class SmsNumberUtils {
+ private static final String TAG = "SmsNumberUtils";
+ private static final boolean DBG = Build.IS_DEBUGGABLE;
+
+ private static final String PLUS_SIGN = "+";
+
+ private static final int NANP_SHORT_LENGTH = 7;
+ private static final int NANP_MEDIUM_LENGTH = 10;
+ private static final int NANP_LONG_LENGTH = 11;
+
+ private static final int NANP_CC = 1;
+ private static final String NANP_NDD = "1";
+ private static final String NANP_IDD = "011";
+
+ private static final int MIN_COUNTRY_AREA_LOCAL_LENGTH = 10;
+
+ private static final int GSM_UMTS_NETWORK = 0;
+ private static final int CDMA_HOME_NETWORK = 1;
+ private static final int CDMA_ROAMING_NETWORK = 2;
+
+ private static final int NP_NONE = 0;
+ private static final int NP_NANP_BEGIN = 1;
+
+ /* <Phone Number>, <NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_LOCAL = NP_NANP_BEGIN;
+
+ /* <Area_code>-<Phone Number>, <NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_AREA_LOCAL = NP_NANP_BEGIN + 1;
+
+ /* <1>-<Area_code>-<Phone Number>, 1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NDD_AREA_LOCAL = NP_NANP_BEGIN + 2;
+
+ /* <+><U.S.Country_code><Area_code><Phone Number>, +1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NBPCD_CC_AREA_LOCAL = NP_NANP_BEGIN + 3;
+
+ /* <Local_IDD><Country_code><Area_code><Phone Number>, 001-1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_LOCALIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 4;
+
+ /* <+><Home_IDD><Country_code><Area_code><Phone Number>, +011-1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 5;
+
+ private static final int NP_INTERNATIONAL_BEGIN = 100;
+ /* <+>-<Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, +011-86-25-86281234 */
+ private static final int NP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN;
+
+ /* <Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, 011-86-25-86281234 */
+ private static final int NP_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 1;
+
+ /* <NBPCD>-<Country_code>-<Area_code>-<Phone Number>, +1-86-25-86281234 */
+ private static final int NP_NBPCD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 2;
+
+ /* <Local_IDD>-<Country_code>-<Area_code>-<Phone Number>, 00-86-25-86281234 */
+ private static final int NP_LOCALIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 3;
+
+ /* <Country_code>-<Area_code>-<Phone Number>, 86-25-86281234*/
+ private static final int NP_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 4;
+
+ private static int[] ALL_COUNTRY_CODES = null;
+ private static int MAX_COUNTRY_CODES_LENGTH;
+ private static HashMap<String, ArrayList<String>> IDDS_MAPS =
+ new HashMap<String, ArrayList<String>>();
+
+ private static class NumberEntry {
+ public String number;
+ public String IDD;
+ public int countryCode;
+ public NumberEntry(String number) {
+ this.number = number;
+ }
+ }
+
+ /**
+ * Breaks the given number down and formats it according to the rules
+ * for different number plans and different network.
+ *
+ * @param number destination number which need to be format
+ * @param activeMcc current network's mcc
+ * @param networkType current network type
+ *
+ * @return the number after formatting.
+ */
+ private static String formatNumber(Context context, String number,
+ String activeMcc,
+ int networkType) {
+ if (number == null ) {
+ throw new IllegalArgumentException("number is null");
+ }
+
+ if (activeMcc == null || activeMcc.trim().length() == 0) {
+ throw new IllegalArgumentException("activeMcc is null or empty!");
+ }
+
+ String networkPortionNumber = PhoneNumberUtils.extractNetworkPortion(number);
+ if (networkPortionNumber == null || networkPortionNumber.length() == 0) {
+ throw new IllegalArgumentException("Number is invalid!");
+ }
+
+ NumberEntry numberEntry = new NumberEntry(networkPortionNumber);
+ ArrayList<String> allIDDs = getAllIDDs(context, activeMcc);
+
+ // First check whether the number is a NANP number.
+ int nanpState = checkNANP(numberEntry, allIDDs);
+ if (DBG) Rlog.d(TAG, "NANP type: " + getNumberPlanType(nanpState));
+
+ if ((nanpState == NP_NANP_LOCAL)
+ || (nanpState == NP_NANP_AREA_LOCAL)
+ || (nanpState == NP_NANP_NDD_AREA_LOCAL)) {
+ return networkPortionNumber;
+ } else if (nanpState == NP_NANP_NBPCD_CC_AREA_LOCAL) {
+ if (networkType == CDMA_HOME_NETWORK
+ || networkType == CDMA_ROAMING_NETWORK) {
+ // Remove "+"
+ return networkPortionNumber.substring(1);
+ } else {
+ return networkPortionNumber;
+ }
+ } else if (nanpState == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
+ if (networkType == CDMA_HOME_NETWORK) {
+ return networkPortionNumber;
+ } else if (networkType == GSM_UMTS_NETWORK) {
+ // Remove the local IDD and replace with "+"
+ int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
+ return PLUS_SIGN + networkPortionNumber.substring(iddLength);
+ } else if (networkType == CDMA_ROAMING_NETWORK) {
+ // Remove the local IDD
+ int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
+ return networkPortionNumber.substring(iddLength);
+ }
+ }
+
+ int internationalState = checkInternationalNumberPlan(context, numberEntry, allIDDs,
+ NANP_IDD);
+ if (DBG) Rlog.d(TAG, "International type: " + getNumberPlanType(internationalState));
+ String returnNumber = null;
+
+ switch (internationalState) {
+ case NP_NBPCD_HOMEIDD_CC_AREA_LOCAL:
+ if (networkType == GSM_UMTS_NETWORK) {
+ // Remove "+"
+ returnNumber = networkPortionNumber.substring(1);
+ }
+ break;
+
+ case NP_NBPCD_CC_AREA_LOCAL:
+ // Replace "+" with "011"
+ returnNumber = NANP_IDD + networkPortionNumber.substring(1);
+ break;
+
+ case NP_LOCALIDD_CC_AREA_LOCAL:
+ if (networkType == GSM_UMTS_NETWORK || networkType == CDMA_ROAMING_NETWORK) {
+ int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
+ // Replace <Local IDD> to <Home IDD>("011")
+ returnNumber = NANP_IDD + networkPortionNumber.substring(iddLength);
+ }
+ break;
+
+ case NP_CC_AREA_LOCAL:
+ int countryCode = numberEntry.countryCode;
+
+ if (!inExceptionListForNpCcAreaLocal(numberEntry)
+ && networkPortionNumber.length() >= 11 && countryCode != NANP_CC) {
+ // Add "011"
+ returnNumber = NANP_IDD + networkPortionNumber;
+ }
+ break;
+
+ case NP_HOMEIDD_CC_AREA_LOCAL:
+ returnNumber = networkPortionNumber;
+ break;
+
+ default:
+ // Replace "+" with 011 in CDMA network if the number's country
+ // code is not in the HbpcdLookup database.
+ if (networkPortionNumber.startsWith(PLUS_SIGN)
+ && (networkType == CDMA_HOME_NETWORK || networkType == CDMA_ROAMING_NETWORK)) {
+ if (networkPortionNumber.startsWith(PLUS_SIGN + NANP_IDD)) {
+ // Only remove "+"
+ returnNumber = networkPortionNumber.substring(1);
+ } else {
+ // Replace "+" with "011"
+ returnNumber = NANP_IDD + networkPortionNumber.substring(1);
+ }
+ }
+ }
+
+ if (returnNumber == null) {
+ returnNumber = networkPortionNumber;
+ }
+ return returnNumber;
+ }
+
+ /**
+ * Query International direct dialing from HbpcdLookup.db
+ * for specified country code
+ *
+ * @param mcc current network's country code
+ *
+ * @return the IDD array list.
+ */
+ private static ArrayList<String> getAllIDDs(Context context, String mcc) {
+ ArrayList<String> allIDDs = IDDS_MAPS.get(mcc);
+ if (allIDDs != null) {
+ return allIDDs;
+ } else {
+ allIDDs = new ArrayList<String>();
+ }
+
+ String projection[] = {MccIdd.IDD, MccIdd.MCC};
+ String where = null;
+
+ // if mcc is null : return all rows
+ // if mcc is empty-string : return those rows whose mcc is emptry-string
+ String[] selectionArgs = null;
+ if (mcc != null) {
+ where = MccIdd.MCC + "=?";
+ selectionArgs = new String[] {mcc};
+ }
+
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(MccIdd.CONTENT_URI, projection,
+ where, selectionArgs, null);
+ if (cursor.getCount() > 0) {
+ while (cursor.moveToNext()) {
+ String idd = cursor.getString(0);
+ if (!allIDDs.contains(idd)) {
+ allIDDs.add(idd);
+ }
+ }
+ }
+ } catch (SQLException e) {
+ Rlog.e(TAG, "Can't access HbpcdLookup database", e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ IDDS_MAPS.put(mcc, allIDDs);
+
+ if (DBG) Rlog.d(TAG, "MCC = " + mcc + ", all IDDs = " + allIDDs);
+ return allIDDs;
+ }
+
+
+ /**
+ * Verify if the the destination number is a NANP number
+ *
+ * @param numberEntry including number and IDD array
+ * @param allIDDs the IDD array list of the current network's country code
+ *
+ * @return the number plan type related NANP
+ */
+ private static int checkNANP(NumberEntry numberEntry, ArrayList<String> allIDDs) {
+ boolean isNANP = false;
+ String number = numberEntry.number;
+
+ if (number.length() == NANP_SHORT_LENGTH) {
+ // 7 digits - Seven digit phone numbers
+ char firstChar = number.charAt(0);
+ if (firstChar >= '2' && firstChar <= '9') {
+ isNANP = true;
+ for (int i=1; i< NANP_SHORT_LENGTH; i++ ) {
+ char c= number.charAt(i);
+ if (!PhoneNumberUtils.isISODigit(c)) {
+ isNANP = false;
+ break;
+ }
+ }
+ }
+ if (isNANP) {
+ return NP_NANP_LOCAL;
+ }
+ } else if (number.length() == NANP_MEDIUM_LENGTH) {
+ // 10 digits - Three digit area code followed by seven digit phone numbers/
+ if (isNANP(number)) {
+ return NP_NANP_AREA_LOCAL;
+ }
+ } else if (number.length() == NANP_LONG_LENGTH) {
+ // 11 digits - One digit U.S. NDD(National Direct Dial) prefix '1',
+ // followed by three digit area code and seven digit phone numbers
+ if (isNANP(number)) {
+ return NP_NANP_NDD_AREA_LOCAL;
+ }
+ } else if (number.startsWith(PLUS_SIGN)) {
+ number = number.substring(1);
+ if (number.length() == NANP_LONG_LENGTH) {
+ // '+' and 11 digits -'+', followed by NANP CC prefix '1' followed by
+ // three digit area code and seven digit phone numbers
+ if (isNANP(number)) {
+ return NP_NANP_NBPCD_CC_AREA_LOCAL;
+ }
+ } else if (number.startsWith(NANP_IDD) && number.length() == NANP_LONG_LENGTH + 3) {
+ // '+' and 14 digits -'+', followed by NANP IDD "011" followed by NANP CC
+ // prefix '1' followed by three digit area code and seven digit phone numbers
+ number = number.substring(3);
+ if (isNANP(number)) {
+ return NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
+ }
+ }
+ } else {
+ // Check whether it's NP_NANP_LOCALIDD_CC_AREA_LOCAL
+ for (String idd : allIDDs) {
+ if (number.startsWith(idd)) {
+ String number2 = number.substring(idd.length());
+ if(number2 !=null && number2.startsWith(String.valueOf(NANP_CC))){
+ if (isNANP(number2)) {
+ numberEntry.IDD = idd;
+ return NP_NANP_LOCALIDD_CC_AREA_LOCAL;
+ }
+ }
+ }
+ }
+ }
+
+ return NP_NONE;
+ }
+
+ private static boolean isNANP(String number) {
+ if (number.length() == NANP_MEDIUM_LENGTH
+ || (number.length() == NANP_LONG_LENGTH && number.startsWith(NANP_NDD))) {
+ if (number.length() == NANP_LONG_LENGTH) {
+ number = number.substring(1);
+ }
+ return (PhoneNumberUtils.isNanp(number));
+ }
+ return false;
+ }
+
+ /**
+ * Verify if the the destination number is an internal number
+ *
+ * @param numberEntry including number and IDD array
+ * @param allIDDs the IDD array list of the current network's country code
+ *
+ * @return the number plan type related international number
+ */
+ private static int checkInternationalNumberPlan(Context context, NumberEntry numberEntry,
+ ArrayList<String> allIDDs,String homeIDD) {
+ String number = numberEntry.number;
+ int countryCode = -1;
+
+ if (number.startsWith(PLUS_SIGN)) {
+ // +xxxxxxxxxx
+ String numberNoNBPCD = number.substring(1);
+ if (numberNoNBPCD.startsWith(homeIDD)) {
+ // +011xxxxxxxx
+ String numberCountryAreaLocal = numberNoNBPCD.substring(homeIDD.length());
+ if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
+ }
+ } else if ((countryCode = getCountryCode(context, numberNoNBPCD)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_NBPCD_CC_AREA_LOCAL;
+ }
+
+ } else if (number.startsWith(homeIDD)) {
+ // 011xxxxxxxxx
+ String numberCountryAreaLocal = number.substring(homeIDD.length());
+ if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_HOMEIDD_CC_AREA_LOCAL;
+ }
+ } else {
+ for (String exitCode : allIDDs) {
+ if (number.startsWith(exitCode)) {
+ String numberNoIDD = number.substring(exitCode.length());
+ if ((countryCode = getCountryCode(context, numberNoIDD)) > 0) {
+ numberEntry.countryCode = countryCode;
+ numberEntry.IDD = exitCode;
+ return NP_LOCALIDD_CC_AREA_LOCAL;
+ }
+ }
+ }
+
+ if (!number.startsWith("0") && (countryCode = getCountryCode(context, number)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_CC_AREA_LOCAL;
+ }
+ }
+ return NP_NONE;
+ }
+
+ /**
+ * Returns the country code from the given number.
+ */
+ private static int getCountryCode(Context context, String number) {
+ int countryCode = -1;
+ if (number.length() >= MIN_COUNTRY_AREA_LOCAL_LENGTH) {
+ // Check Country code
+ int[] allCCs = getAllCountryCodes(context);
+ if (allCCs == null) {
+ return countryCode;
+ }
+
+ int[] ccArray = new int[MAX_COUNTRY_CODES_LENGTH];
+ for (int i = 0; i < MAX_COUNTRY_CODES_LENGTH; i ++) {
+ ccArray[i] = Integer.parseInt(number.substring(0, i+1));
+ }
+
+ for (int i = 0; i < allCCs.length; i ++) {
+ int tempCC = allCCs[i];
+ for (int j = 0; j < MAX_COUNTRY_CODES_LENGTH; j ++) {
+ if (tempCC == ccArray[j]) {
+ if (DBG) Rlog.d(TAG, "Country code = " + tempCC);
+ return tempCC;
+ }
+ }
+ }
+ }
+
+ return countryCode;
+ }
+
+ /**
+ * Gets all country Codes information with given MCC.
+ */
+ private static int[] getAllCountryCodes(Context context) {
+ if (ALL_COUNTRY_CODES != null) {
+ return ALL_COUNTRY_CODES;
+ }
+
+ Cursor cursor = null;
+ try {
+ String projection[] = {MccLookup.COUNTRY_CODE};
+ cursor = context.getContentResolver().query(MccLookup.CONTENT_URI,
+ projection, null, null, null);
+
+ if (cursor.getCount() > 0) {
+ ALL_COUNTRY_CODES = new int[cursor.getCount()];
+ int i = 0;
+ while (cursor.moveToNext()) {
+ int countryCode = cursor.getInt(0);
+ ALL_COUNTRY_CODES[i++] = countryCode;
+ int length = String.valueOf(countryCode).trim().length();
+ if (length > MAX_COUNTRY_CODES_LENGTH) {
+ MAX_COUNTRY_CODES_LENGTH = length;
+ }
+ }
+ }
+ } catch (SQLException e) {
+ Rlog.e(TAG, "Can't access HbpcdLookup database", e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return ALL_COUNTRY_CODES;
+ }
+
+ private static boolean inExceptionListForNpCcAreaLocal(NumberEntry numberEntry) {
+ int countryCode = numberEntry.countryCode;
+ boolean result = (numberEntry.number.length() == 12
+ && (countryCode == 7 || countryCode == 20
+ || countryCode == 65 || countryCode == 90));
+ return result;
+ }
+
+ private static String getNumberPlanType(int state) {
+ String numberPlanType = "Number Plan type (" + state + "): ";
+
+ if (state == NP_NANP_LOCAL) {
+ numberPlanType = "NP_NANP_LOCAL";
+ } else if (state == NP_NANP_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_AREA_LOCAL";
+ } else if (state == NP_NANP_NDD_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_NDD_AREA_LOCAL";
+ } else if (state == NP_NANP_NBPCD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_NBPCD_CC_AREA_LOCAL";
+ } else if (state == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_LOCALIDD_CC_AREA_LOCAL";
+ } else if (state == NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL";
+ } else if (state == NP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NBPCD_HOMEIDD_CC_AREA_LOCAL";
+ } else if (state == NP_HOMEIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_HOMEIDD_CC_AREA_LOCAL";
+ } else if (state == NP_NBPCD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NBPCD_CC_AREA_LOCAL";
+ } else if (state == NP_LOCALIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_LOCALIDD_CC_AREA_LOCAL";
+ } else if (state == NP_CC_AREA_LOCAL) {
+ numberPlanType = "NP_CC_AREA_LOCAL";
+ } else {
+ numberPlanType = "Unknown type";
+ }
+ return numberPlanType;
+ }
+
+ /**
+ * Filter the destination number if using VZW sim card.
+ */
+ public static String filterDestAddr(Context context, int subId, String destAddr) {
+ if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + Rlog.pii(TAG, destAddr) + "\"" );
+
+ if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
+ Rlog.w(TAG, "destAddr" + Rlog.pii(TAG, destAddr) +
+ " is not a global phone number! Nothing changed.");
+ return destAddr;
+ }
+
+ final TelephonyManager telephonyManager = ((TelephonyManager) context
+ .getSystemService(Context.TELEPHONY_SERVICE)).createForSubscriptionId(subId);
+ final String networkOperator = telephonyManager.getNetworkOperator();
+ String result = null;
+
+ if (needToConvert(context, subId)) {
+ final int networkType = getNetworkType(telephonyManager);
+ if (networkType != -1 && !TextUtils.isEmpty(networkOperator)) {
+ String networkMcc = networkOperator.substring(0, 3);
+ if (networkMcc != null && networkMcc.trim().length() > 0) {
+ result = formatNumber(context, destAddr, networkMcc, networkType);
+ }
+ }
+ }
+
+ if (DBG) {
+ Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
+ Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? Rlog.pii(TAG,
+ result) : Rlog.pii(TAG, destAddr)) + "\"");
+ }
+ return result != null ? result : destAddr;
+ }
+
+ /**
+ * Returns the current network type
+ */
+ private static int getNetworkType(TelephonyManager telephonyManager) {
+ int networkType = -1;
+ int phoneType = telephonyManager.getPhoneType();
+
+ if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
+ networkType = GSM_UMTS_NETWORK;
+ } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
+ if (isInternationalRoaming(telephonyManager)) {
+ networkType = CDMA_ROAMING_NETWORK;
+ } else {
+ networkType = CDMA_HOME_NETWORK;
+ }
+ } else {
+ if (DBG) Rlog.w(TAG, "warning! unknown mPhoneType value=" + phoneType);
+ }
+
+ return networkType;
+ }
+
+ private static boolean isInternationalRoaming(TelephonyManager telephonyManager) {
+ String operatorIsoCountry = telephonyManager.getNetworkCountryIso();
+ String simIsoCountry = telephonyManager.getSimCountryIso();
+ boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoCountry)
+ && !TextUtils.isEmpty(simIsoCountry)
+ && !simIsoCountry.equals(operatorIsoCountry);
+ if (internationalRoaming) {
+ if ("us".equals(simIsoCountry)) {
+ internationalRoaming = !"vi".equals(operatorIsoCountry);
+ } else if ("vi".equals(simIsoCountry)) {
+ internationalRoaming = !"us".equals(operatorIsoCountry);
+ }
+ }
+ return internationalRoaming;
+ }
+
+ private static boolean needToConvert(Context context, int subId) {
+ // Calling package may not have READ_PHONE_STATE which is required for getConfig().
+ // Clear the calling identity so that it is called as self.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle bundle = configManager.getConfigForSubId(subId);
+ if (bundle != null) {
+ return bundle.getBoolean(CarrierConfigManager
+ .KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ // by default this value is false
+ return false;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/ContentType.java b/telephony/common/com/google/android/mms/ContentType.java
new file mode 100644
index 0000000..12e4b7e
--- /dev/null
+++ b/telephony/common/com/google/android/mms/ContentType.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 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.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.ArrayList;
+
+public class ContentType {
+ public static final String MMS_MESSAGE = "application/vnd.wap.mms-message";
+ // The phony content type for generic PDUs (e.g. ReadOrig.ind,
+ // Notification.ind, Delivery.ind).
+ public static final String MMS_GENERIC = "application/vnd.wap.mms-generic";
+ public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed";
+ public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
+ public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
+
+ public static final String TEXT_PLAIN = "text/plain";
+ public static final String TEXT_HTML = "text/html";
+ public static final String TEXT_VCALENDAR = "text/x-vCalendar";
+ public static final String TEXT_VCARD = "text/x-vCard";
+
+ public static final String IMAGE_UNSPECIFIED = "image/*";
+ public static final String IMAGE_JPEG = "image/jpeg";
+ public static final String IMAGE_JPG = "image/jpg";
+ public static final String IMAGE_GIF = "image/gif";
+ public static final String IMAGE_WBMP = "image/vnd.wap.wbmp";
+ public static final String IMAGE_PNG = "image/png";
+ public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp";
+
+ public static final String AUDIO_UNSPECIFIED = "audio/*";
+ public static final String AUDIO_AAC = "audio/aac";
+ public static final String AUDIO_AMR = "audio/amr";
+ public static final String AUDIO_IMELODY = "audio/imelody";
+ public static final String AUDIO_MID = "audio/mid";
+ public static final String AUDIO_MIDI = "audio/midi";
+ public static final String AUDIO_MP3 = "audio/mp3";
+ public static final String AUDIO_MPEG3 = "audio/mpeg3";
+ public static final String AUDIO_MPEG = "audio/mpeg";
+ public static final String AUDIO_MPG = "audio/mpg";
+ public static final String AUDIO_MP4 = "audio/mp4";
+ public static final String AUDIO_X_MID = "audio/x-mid";
+ public static final String AUDIO_X_MIDI = "audio/x-midi";
+ public static final String AUDIO_X_MP3 = "audio/x-mp3";
+ public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3";
+ public static final String AUDIO_X_MPEG = "audio/x-mpeg";
+ public static final String AUDIO_X_MPG = "audio/x-mpg";
+ public static final String AUDIO_3GPP = "audio/3gpp";
+ public static final String AUDIO_X_WAV = "audio/x-wav";
+ public static final String AUDIO_OGG = "application/ogg";
+ public static final String AUDIO_OGG2 = "audio/ogg";
+
+ public static final String VIDEO_UNSPECIFIED = "video/*";
+ public static final String VIDEO_3GPP = "video/3gpp";
+ public static final String VIDEO_3G2 = "video/3gpp2";
+ public static final String VIDEO_H263 = "video/h263";
+ public static final String VIDEO_MP4 = "video/mp4";
+
+ public static final String APP_SMIL = "application/smil";
+ public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml";
+ public static final String APP_XHTML = "application/xhtml+xml";
+
+ public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content";
+ public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ private static final ArrayList<String> sSupportedContentTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedImageTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedAudioTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedVideoTypes = new ArrayList<String>();
+
+ static {
+ sSupportedContentTypes.add(TEXT_PLAIN);
+ sSupportedContentTypes.add(TEXT_HTML);
+ sSupportedContentTypes.add(TEXT_VCALENDAR);
+ sSupportedContentTypes.add(TEXT_VCARD);
+
+ sSupportedContentTypes.add(IMAGE_JPEG);
+ sSupportedContentTypes.add(IMAGE_GIF);
+ sSupportedContentTypes.add(IMAGE_WBMP);
+ sSupportedContentTypes.add(IMAGE_PNG);
+ sSupportedContentTypes.add(IMAGE_JPG);
+ sSupportedContentTypes.add(IMAGE_X_MS_BMP);
+ //supportedContentTypes.add(IMAGE_SVG); not yet supported.
+
+ sSupportedContentTypes.add(AUDIO_AAC);
+ sSupportedContentTypes.add(AUDIO_AMR);
+ sSupportedContentTypes.add(AUDIO_IMELODY);
+ sSupportedContentTypes.add(AUDIO_MID);
+ sSupportedContentTypes.add(AUDIO_MIDI);
+ sSupportedContentTypes.add(AUDIO_MP3);
+ sSupportedContentTypes.add(AUDIO_MP4);
+ sSupportedContentTypes.add(AUDIO_MPEG3);
+ sSupportedContentTypes.add(AUDIO_MPEG);
+ sSupportedContentTypes.add(AUDIO_MPG);
+ sSupportedContentTypes.add(AUDIO_X_MID);
+ sSupportedContentTypes.add(AUDIO_X_MIDI);
+ sSupportedContentTypes.add(AUDIO_X_MP3);
+ sSupportedContentTypes.add(AUDIO_X_MPEG3);
+ sSupportedContentTypes.add(AUDIO_X_MPEG);
+ sSupportedContentTypes.add(AUDIO_X_MPG);
+ sSupportedContentTypes.add(AUDIO_X_WAV);
+ sSupportedContentTypes.add(AUDIO_3GPP);
+ sSupportedContentTypes.add(AUDIO_OGG);
+ sSupportedContentTypes.add(AUDIO_OGG2);
+
+ sSupportedContentTypes.add(VIDEO_3GPP);
+ sSupportedContentTypes.add(VIDEO_3G2);
+ sSupportedContentTypes.add(VIDEO_H263);
+ sSupportedContentTypes.add(VIDEO_MP4);
+
+ sSupportedContentTypes.add(APP_SMIL);
+ sSupportedContentTypes.add(APP_WAP_XHTML);
+ sSupportedContentTypes.add(APP_XHTML);
+
+ sSupportedContentTypes.add(APP_DRM_CONTENT);
+ sSupportedContentTypes.add(APP_DRM_MESSAGE);
+
+ // add supported image types
+ sSupportedImageTypes.add(IMAGE_JPEG);
+ sSupportedImageTypes.add(IMAGE_GIF);
+ sSupportedImageTypes.add(IMAGE_WBMP);
+ sSupportedImageTypes.add(IMAGE_PNG);
+ sSupportedImageTypes.add(IMAGE_JPG);
+ sSupportedImageTypes.add(IMAGE_X_MS_BMP);
+
+ // add supported audio types
+ sSupportedAudioTypes.add(AUDIO_AAC);
+ sSupportedAudioTypes.add(AUDIO_AMR);
+ sSupportedAudioTypes.add(AUDIO_IMELODY);
+ sSupportedAudioTypes.add(AUDIO_MID);
+ sSupportedAudioTypes.add(AUDIO_MIDI);
+ sSupportedAudioTypes.add(AUDIO_MP3);
+ sSupportedAudioTypes.add(AUDIO_MPEG3);
+ sSupportedAudioTypes.add(AUDIO_MPEG);
+ sSupportedAudioTypes.add(AUDIO_MPG);
+ sSupportedAudioTypes.add(AUDIO_MP4);
+ sSupportedAudioTypes.add(AUDIO_X_MID);
+ sSupportedAudioTypes.add(AUDIO_X_MIDI);
+ sSupportedAudioTypes.add(AUDIO_X_MP3);
+ sSupportedAudioTypes.add(AUDIO_X_MPEG3);
+ sSupportedAudioTypes.add(AUDIO_X_MPEG);
+ sSupportedAudioTypes.add(AUDIO_X_MPG);
+ sSupportedAudioTypes.add(AUDIO_X_WAV);
+ sSupportedAudioTypes.add(AUDIO_3GPP);
+ sSupportedAudioTypes.add(AUDIO_OGG);
+ sSupportedAudioTypes.add(AUDIO_OGG2);
+
+ // add supported video types
+ sSupportedVideoTypes.add(VIDEO_3GPP);
+ sSupportedVideoTypes.add(VIDEO_3G2);
+ sSupportedVideoTypes.add(VIDEO_H263);
+ sSupportedVideoTypes.add(VIDEO_MP4);
+ }
+
+ // This class should never be instantiated.
+ private ContentType() {
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedType(String contentType) {
+ return (null != contentType) && sSupportedContentTypes.contains(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedImageType(String contentType) {
+ return isImageType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedAudioType(String contentType) {
+ return isAudioType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedVideoType(String contentType) {
+ return isVideoType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isTextType(String contentType) {
+ return (null != contentType) && contentType.startsWith("text/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isImageType(String contentType) {
+ return (null != contentType) && contentType.startsWith("image/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isAudioType(String contentType) {
+ return (null != contentType) && contentType.startsWith("audio/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isVideoType(String contentType) {
+ return (null != contentType) && contentType.startsWith("video/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isDrmType(String contentType) {
+ return (null != contentType)
+ && (contentType.equals(APP_DRM_CONTENT)
+ || contentType.equals(APP_DRM_MESSAGE));
+ }
+
+ public static boolean isUnspecified(String contentType) {
+ return (null != contentType) && contentType.endsWith("*");
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getImageTypes() {
+ return (ArrayList<String>) sSupportedImageTypes.clone();
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getAudioTypes() {
+ return (ArrayList<String>) sSupportedAudioTypes.clone();
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getVideoTypes() {
+ return (ArrayList<String>) sSupportedVideoTypes.clone();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getSupportedTypes() {
+ return (ArrayList<String>) sSupportedContentTypes.clone();
+ }
+}
diff --git a/telephony/common/com/google/android/mms/InvalidHeaderValueException.java b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java
new file mode 100644
index 0000000..2836c30
--- /dev/null
+++ b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+/**
+ * Thrown when an invalid header value was set.
+ */
+public class InvalidHeaderValueException extends MmsException {
+ private static final long serialVersionUID = -2053384496042052262L;
+
+ /**
+ * Constructs an InvalidHeaderValueException with no detailed message.
+ */
+ public InvalidHeaderValueException() {
+ super();
+ }
+
+ /**
+ * Constructs an InvalidHeaderValueException with the specified detailed message.
+ *
+ * @param message the detailed message.
+ */
+ @UnsupportedAppUsage
+ public InvalidHeaderValueException(String message) {
+ super(message);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/MmsException.java b/telephony/common/com/google/android/mms/MmsException.java
new file mode 100644
index 0000000..5be33ed
--- /dev/null
+++ b/telephony/common/com/google/android/mms/MmsException.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+/**
+ * A generic exception that is thrown by the Mms client.
+ */
+public class MmsException extends Exception {
+ private static final long serialVersionUID = -7323249827281485390L;
+
+ /**
+ * Creates a new MmsException.
+ */
+ @UnsupportedAppUsage
+ public MmsException() {
+ super();
+ }
+
+ /**
+ * Creates a new MmsException with the specified detail message.
+ *
+ * @param message the detail message.
+ */
+ @UnsupportedAppUsage
+ public MmsException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new MmsException with the specified cause.
+ *
+ * @param cause the cause.
+ */
+ @UnsupportedAppUsage
+ public MmsException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new MmsException with the specified detail message and cause.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ @UnsupportedAppUsage
+ public MmsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/package.html b/telephony/common/com/google/android/mms/package.html
new file mode 100755
index 0000000..c9f96a6
--- /dev/null
+++ b/telephony/common/com/google/android/mms/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java
new file mode 100644
index 0000000..ae447d7
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Acknowledge.ind PDU.
+ */
+public class AcknowledgeInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-Acknowledge.ind pdu.
+ *
+ * @param mmsVersion current viersion of mms
+ * @param transactionId the transaction-id value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if transactionId is null.
+ */
+ @UnsupportedAppUsage
+ public AcknowledgeInd(int mmsVersion, byte[] transactionId)
+ throws InvalidHeaderValueException {
+ super();
+
+ setMessageType(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ AcknowledgeInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Report-Allowed field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public int getReportAllowed() {
+ return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Report-Allowed field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReportAllowed(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/Base64.java b/telephony/common/com/google/android/mms/pdu/Base64.java
new file mode 100644
index 0000000..483fa7f
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/Base64.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class Base64 {
+ /**
+ * Used to get the number of Quadruples.
+ */
+ static final int FOURBYTE = 4;
+
+ /**
+ * Byte used to pad output.
+ */
+ static final byte PAD = (byte) '=';
+
+ /**
+ * The base length.
+ */
+ static final int BASELENGTH = 255;
+
+ // Create arrays to hold the base64 characters
+ private static byte[] base64Alphabet = new byte[BASELENGTH];
+
+ // Populating the character arrays
+ static {
+ for (int i = 0; i < BASELENGTH; i++) {
+ base64Alphabet[i] = (byte) -1;
+ }
+ for (int i = 'Z'; i >= 'A'; i--) {
+ base64Alphabet[i] = (byte) (i - 'A');
+ }
+ for (int i = 'z'; i >= 'a'; i--) {
+ base64Alphabet[i] = (byte) (i - 'a' + 26);
+ }
+ for (int i = '9'; i >= '0'; i--) {
+ base64Alphabet[i] = (byte) (i - '0' + 52);
+ }
+
+ base64Alphabet['+'] = 62;
+ base64Alphabet['/'] = 63;
+ }
+
+ /**
+ * Decodes Base64 data into octects
+ *
+ * @param base64Data Byte array containing Base64 data
+ * @return Array containing decoded data.
+ */
+ @UnsupportedAppUsage
+ public static byte[] decodeBase64(byte[] base64Data) {
+ // RFC 2045 requires that we discard ALL non-Base64 characters
+ base64Data = discardNonBase64(base64Data);
+
+ // handle the edge case, so we don't have to worry about it later
+ if (base64Data.length == 0) {
+ return new byte[0];
+ }
+
+ int numberQuadruple = base64Data.length / FOURBYTE;
+ byte decodedData[] = null;
+ byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+ // Throw away anything not in base64Data
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ {
+ // this sizes the output array properly - rlw
+ int lastData = base64Data.length;
+ // ignore the '=' padding
+ while (base64Data[lastData - 1] == PAD) {
+ if (--lastData == 0) {
+ return new byte[0];
+ }
+ }
+ decodedData = new byte[lastData - numberQuadruple];
+ }
+
+ for (int i = 0; i < numberQuadruple; i++) {
+ dataIndex = i * 4;
+ marker0 = base64Data[dataIndex + 2];
+ marker1 = base64Data[dataIndex + 3];
+
+ b1 = base64Alphabet[base64Data[dataIndex]];
+ b2 = base64Alphabet[base64Data[dataIndex + 1]];
+
+ if (marker0 != PAD && marker1 != PAD) {
+ //No PAD e.g 3cQl
+ b3 = base64Alphabet[marker0];
+ b4 = base64Alphabet[marker1];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
+ } else if (marker0 == PAD) {
+ //Two PAD e.g. 3c[Pad][Pad]
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ } else if (marker1 == PAD) {
+ //One PAD e.g. 3cQ[Pad]
+ b3 = base64Alphabet[marker0];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ }
+ encodedIndex += 3;
+ }
+ return decodedData;
+ }
+
+ /**
+ * Check octect whether it is a base64 encoding.
+ *
+ * @param octect to be checked byte
+ * @return ture if it is base64 encoding, false otherwise.
+ */
+ private static boolean isBase64(byte octect) {
+ if (octect == PAD) {
+ return true;
+ } else if (base64Alphabet[octect] == -1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Discards any characters outside of the base64 alphabet, per
+ * the requirements on page 25 of RFC 2045 - "Any characters
+ * outside of the base64 alphabet are to be ignored in base64
+ * encoded data."
+ *
+ * @param data The base-64 encoded data to groom
+ * @return The data, less non-base64 characters (see RFC 2045).
+ */
+ static byte[] discardNonBase64(byte[] data) {
+ byte groomedData[] = new byte[data.length];
+ int bytesCopied = 0;
+
+ for (int i = 0; i < data.length; i++) {
+ if (isBase64(data[i])) {
+ groomedData[bytesCopied++] = data[i];
+ }
+ }
+
+ byte packedData[] = new byte[bytesCopied];
+
+ System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+ return packedData;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/CharacterSets.java b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
new file mode 100644
index 0000000..27da35e
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+
+public class CharacterSets {
+ /**
+ * IANA assigned MIB enum numbers.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * Any-charset = <Octet 128>
+ * Equivalent to the special RFC2616 charset value "*"
+ */
+ public static final int ANY_CHARSET = 0x00;
+ public static final int US_ASCII = 0x03;
+ public static final int ISO_8859_1 = 0x04;
+ public static final int ISO_8859_2 = 0x05;
+ public static final int ISO_8859_3 = 0x06;
+ public static final int ISO_8859_4 = 0x07;
+ public static final int ISO_8859_5 = 0x08;
+ public static final int ISO_8859_6 = 0x09;
+ public static final int ISO_8859_7 = 0x0A;
+ public static final int ISO_8859_8 = 0x0B;
+ public static final int ISO_8859_9 = 0x0C;
+ public static final int SHIFT_JIS = 0x11;
+ public static final int UTF_8 = 0x6A;
+ public static final int BIG5 = 0x07EA;
+ public static final int UCS2 = 0x03E8;
+ public static final int UTF_16 = 0x03F7;
+
+ /**
+ * If the encoding of given data is unsupported, use UTF_8 to decode it.
+ */
+ public static final int DEFAULT_CHARSET = UTF_8;
+
+ /**
+ * Array of MIB enum numbers.
+ */
+ private static final int[] MIBENUM_NUMBERS = {
+ ANY_CHARSET,
+ US_ASCII,
+ ISO_8859_1,
+ ISO_8859_2,
+ ISO_8859_3,
+ ISO_8859_4,
+ ISO_8859_5,
+ ISO_8859_6,
+ ISO_8859_7,
+ ISO_8859_8,
+ ISO_8859_9,
+ SHIFT_JIS,
+ UTF_8,
+ BIG5,
+ UCS2,
+ UTF_16,
+ };
+
+ /**
+ * The Well-known-charset Mime name.
+ */
+ public static final String MIMENAME_ANY_CHARSET = "*";
+ public static final String MIMENAME_US_ASCII = "us-ascii";
+ public static final String MIMENAME_ISO_8859_1 = "iso-8859-1";
+ public static final String MIMENAME_ISO_8859_2 = "iso-8859-2";
+ public static final String MIMENAME_ISO_8859_3 = "iso-8859-3";
+ public static final String MIMENAME_ISO_8859_4 = "iso-8859-4";
+ public static final String MIMENAME_ISO_8859_5 = "iso-8859-5";
+ public static final String MIMENAME_ISO_8859_6 = "iso-8859-6";
+ public static final String MIMENAME_ISO_8859_7 = "iso-8859-7";
+ public static final String MIMENAME_ISO_8859_8 = "iso-8859-8";
+ public static final String MIMENAME_ISO_8859_9 = "iso-8859-9";
+ public static final String MIMENAME_SHIFT_JIS = "shift_JIS";
+ public static final String MIMENAME_UTF_8 = "utf-8";
+ public static final String MIMENAME_BIG5 = "big5";
+ public static final String MIMENAME_UCS2 = "iso-10646-ucs-2";
+ public static final String MIMENAME_UTF_16 = "utf-16";
+
+ public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8;
+
+ /**
+ * Array of the names of character sets.
+ */
+ private static final String[] MIME_NAMES = {
+ MIMENAME_ANY_CHARSET,
+ MIMENAME_US_ASCII,
+ MIMENAME_ISO_8859_1,
+ MIMENAME_ISO_8859_2,
+ MIMENAME_ISO_8859_3,
+ MIMENAME_ISO_8859_4,
+ MIMENAME_ISO_8859_5,
+ MIMENAME_ISO_8859_6,
+ MIMENAME_ISO_8859_7,
+ MIMENAME_ISO_8859_8,
+ MIMENAME_ISO_8859_9,
+ MIMENAME_SHIFT_JIS,
+ MIMENAME_UTF_8,
+ MIMENAME_BIG5,
+ MIMENAME_UCS2,
+ MIMENAME_UTF_16,
+ };
+
+ private static final HashMap<Integer, String> MIBENUM_TO_NAME_MAP;
+ private static final HashMap<String, Integer> NAME_TO_MIBENUM_MAP;
+
+ static {
+ // Create the HashMaps.
+ MIBENUM_TO_NAME_MAP = new HashMap<Integer, String>();
+ NAME_TO_MIBENUM_MAP = new HashMap<String, Integer>();
+ assert(MIBENUM_NUMBERS.length == MIME_NAMES.length);
+ int count = MIBENUM_NUMBERS.length - 1;
+ for(int i = 0; i <= count; i++) {
+ MIBENUM_TO_NAME_MAP.put(MIBENUM_NUMBERS[i], MIME_NAMES[i]);
+ NAME_TO_MIBENUM_MAP.put(MIME_NAMES[i], MIBENUM_NUMBERS[i]);
+ }
+ }
+
+ private CharacterSets() {} // Non-instantiatable
+
+ /**
+ * Map an MIBEnum number to the name of the charset which this number
+ * is assigned to by IANA.
+ *
+ * @param mibEnumValue An IANA assigned MIBEnum number.
+ * @return The name string of the charset.
+ * @throws UnsupportedEncodingException
+ */
+ @UnsupportedAppUsage
+ public static String getMimeName(int mibEnumValue)
+ throws UnsupportedEncodingException {
+ String name = MIBENUM_TO_NAME_MAP.get(mibEnumValue);
+ if (name == null) {
+ throw new UnsupportedEncodingException();
+ }
+ return name;
+ }
+
+ /**
+ * Map a well-known charset name to its assigned MIBEnum number.
+ *
+ * @param mimeName The charset name.
+ * @return The MIBEnum number assigned by IANA for this charset.
+ * @throws UnsupportedEncodingException
+ */
+ @UnsupportedAppUsage
+ public static int getMibEnumValue(String mimeName)
+ throws UnsupportedEncodingException {
+ if(null == mimeName) {
+ return -1;
+ }
+
+ Integer mibEnumValue = NAME_TO_MIBENUM_MAP.get(mimeName);
+ if (mibEnumValue == null) {
+ throw new UnsupportedEncodingException();
+ }
+ return mibEnumValue;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/DeliveryInd.java b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java
new file mode 100644
index 0000000..7093ac6
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Delivery.Ind Pdu.
+ */
+public class DeliveryInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ public DeliveryInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_DELIVERY_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ DeliveryInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value, should not be null
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get Status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getStatus() {
+ return mPduHeaders.getOctet(PduHeaders.STATUS);
+ }
+
+ /**
+ * Set Status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.STATUS);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public EncodedStringValue getStatusText() {return null;}
+ * public void setStatusText(EncodedStringValue value) {}
+ */
+}
diff --git a/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java
new file mode 100644
index 0000000..4166275
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 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.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+/**
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+public class EncodedStringValue implements Cloneable {
+ private static final String TAG = "EncodedStringValue";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * The Char-set value.
+ */
+ private int mCharacterSet;
+
+ /**
+ * The Text-string value.
+ */
+ private byte[] mData;
+
+ /**
+ * Constructor.
+ *
+ * @param charset the Char-set value
+ * @param data the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue(int charset, byte[] data) {
+ // TODO: CharSet needs to be validated against MIBEnum.
+ if(null == data) {
+ throw new NullPointerException("EncodedStringValue: Text-string is null.");
+ }
+
+ mCharacterSet = charset;
+ mData = new byte[data.length];
+ System.arraycopy(data, 0, mData, 0, data.length);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param data the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue(byte[] data) {
+ this(CharacterSets.DEFAULT_CHARSET, data);
+ }
+
+ @UnsupportedAppUsage
+ public EncodedStringValue(String data) {
+ try {
+ mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
+ mCharacterSet = CharacterSets.DEFAULT_CHARSET;
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Default encoding must be supported.", e);
+ }
+ }
+
+ /**
+ * Get Char-set value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getCharacterSet() {
+ return mCharacterSet;
+ }
+
+ /**
+ * Set Char-set value.
+ *
+ * @param charset the Char-set value
+ */
+ @UnsupportedAppUsage
+ public void setCharacterSet(int charset) {
+ // TODO: CharSet needs to be validated against MIBEnum.
+ mCharacterSet = charset;
+ }
+
+ /**
+ * Get Text-string value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTextString() {
+ byte[] byteArray = new byte[mData.length];
+
+ System.arraycopy(mData, 0, byteArray, 0, mData.length);
+ return byteArray;
+ }
+
+ /**
+ * Set Text-string value.
+ *
+ * @param textString the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTextString(byte[] textString) {
+ if(null == textString) {
+ throw new NullPointerException("EncodedStringValue: Text-string is null.");
+ }
+
+ mData = new byte[textString.length];
+ System.arraycopy(textString, 0, mData, 0, textString.length);
+ }
+
+ /**
+ * Convert this object to a {@link java.lang.String}. If the encoding of
+ * the EncodedStringValue is null or unsupported, it will be
+ * treated as iso-8859-1 encoding.
+ *
+ * @return The decoded String.
+ */
+ @UnsupportedAppUsage
+ public String getString() {
+ if (CharacterSets.ANY_CHARSET == mCharacterSet) {
+ return new String(mData); // system default encoding.
+ } else {
+ try {
+ String name = CharacterSets.getMimeName(mCharacterSet);
+ return new String(mData, name);
+ } catch (UnsupportedEncodingException e) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, e.getMessage(), e);
+ }
+ try {
+ return new String(mData, CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e2) {
+ return new String(mData); // system default encoding.
+ }
+ }
+ }
+ }
+
+ /**
+ * Append to Text-string.
+ *
+ * @param textString the textString to append
+ * @throws NullPointerException if the text String is null
+ * or an IOException occurred.
+ */
+ @UnsupportedAppUsage
+ public void appendTextString(byte[] textString) {
+ if(null == textString) {
+ throw new NullPointerException("Text-string is null.");
+ }
+
+ if(null == mData) {
+ mData = new byte[textString.length];
+ System.arraycopy(textString, 0, mData, 0, textString.length);
+ } else {
+ ByteArrayOutputStream newTextString = new ByteArrayOutputStream();
+ try {
+ newTextString.write(mData);
+ newTextString.write(textString);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new NullPointerException(
+ "appendTextString: failed when write a new Text-string");
+ }
+
+ mData = newTextString.toByteArray();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ super.clone();
+ int len = mData.length;
+ byte[] dstBytes = new byte[len];
+ System.arraycopy(mData, 0, dstBytes, 0, len);
+
+ try {
+ return new EncodedStringValue(mCharacterSet, dstBytes);
+ } catch (Exception e) {
+ Log.e(TAG, "failed to clone an EncodedStringValue: " + this);
+ e.printStackTrace();
+ throw new CloneNotSupportedException(e.getMessage());
+ }
+ }
+
+ /**
+ * Split this encoded string around matches of the given pattern.
+ *
+ * @param pattern the delimiting pattern
+ * @return the array of encoded strings computed by splitting this encoded
+ * string around matches of the given pattern
+ */
+ public EncodedStringValue[] split(String pattern) {
+ String[] temp = getString().split(pattern);
+ EncodedStringValue[] ret = new EncodedStringValue[temp.length];
+ for (int i = 0; i < ret.length; ++i) {
+ try {
+ ret[i] = new EncodedStringValue(mCharacterSet,
+ temp[i].getBytes());
+ } catch (NullPointerException e) {
+ // Can't arrive here
+ return null;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Extract an EncodedStringValue[] from a given String.
+ */
+ @UnsupportedAppUsage
+ public static EncodedStringValue[] extract(String src) {
+ String[] values = src.split(";");
+
+ ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].length() > 0) {
+ list.add(new EncodedStringValue(values[i]));
+ }
+ }
+
+ int len = list.size();
+ if (len > 0) {
+ return list.toArray(new EncodedStringValue[len]);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Concatenate an EncodedStringValue[] into a single String.
+ */
+ @UnsupportedAppUsage
+ public static String concat(EncodedStringValue[] addr) {
+ StringBuilder sb = new StringBuilder();
+ int maxIndex = addr.length - 1;
+ for (int i = 0; i <= maxIndex; i++) {
+ sb.append(addr[i].getString());
+ if (i < maxIndex) {
+ sb.append(";");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @UnsupportedAppUsage
+ public static EncodedStringValue copy(EncodedStringValue value) {
+ if (value == null) {
+ return null;
+ }
+
+ return new EncodedStringValue(value.mCharacterSet, value.mData);
+ }
+
+ @UnsupportedAppUsage
+ public static EncodedStringValue[] encodeStrings(String[] array) {
+ int count = array.length;
+ if (count > 0) {
+ EncodedStringValue[] encodedArray = new EncodedStringValue[count];
+ for (int i = 0; i < count; i++) {
+ encodedArray[i] = new EncodedStringValue(array[i]);
+ }
+ return encodedArray;
+ }
+ return null;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/GenericPdu.java b/telephony/common/com/google/android/mms/pdu/GenericPdu.java
new file mode 100644
index 0000000..ebf16ac
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/GenericPdu.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class GenericPdu {
+ /**
+ * The headers of pdu.
+ */
+ @UnsupportedAppUsage
+ PduHeaders mPduHeaders = null;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu() {
+ mPduHeaders = new PduHeaders();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param headers Headers for this PDU.
+ */
+ GenericPdu(PduHeaders headers) {
+ mPduHeaders = headers;
+ }
+
+ /**
+ * Get the headers of this PDU.
+ *
+ * @return A PduHeaders of this PDU.
+ */
+ @UnsupportedAppUsage
+ PduHeaders getPduHeaders() {
+ return mPduHeaders;
+ }
+
+ /**
+ * Get X-Mms-Message-Type field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public int getMessageType() {
+ return mPduHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
+ }
+
+ /**
+ * Set X-Mms-Message-Type field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if field's value is not Octet.
+ */
+ @UnsupportedAppUsage
+ public void setMessageType(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.MESSAGE_TYPE);
+ }
+
+ /**
+ * Get X-Mms-MMS-Version field value.
+ *
+ * @return the X-Mms-MMS-Version value
+ */
+ public int getMmsVersion() {
+ return mPduHeaders.getOctet(PduHeaders.MMS_VERSION);
+ }
+
+ /**
+ * Set X-Mms-MMS-Version field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if field's value is not Octet.
+ */
+ public void setMmsVersion(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java
new file mode 100644
index 0000000..e108f76
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * Multimedia message PDU.
+ */
+public class MultimediaMessagePdu extends GenericPdu{
+ /**
+ * The body.
+ */
+ private PduBody mMessageBody;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public MultimediaMessagePdu() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param header the header of this PDU
+ * @param body the body of this PDU
+ */
+ @UnsupportedAppUsage
+ public MultimediaMessagePdu(PduHeaders header, PduBody body) {
+ super(header);
+ mMessageBody = body;
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ MultimediaMessagePdu(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get body of the PDU.
+ *
+ * @return the body
+ */
+ @UnsupportedAppUsage
+ public PduBody getBody() {
+ return mMessageBody;
+ }
+
+ /**
+ * Set body of the PDU.
+ *
+ * @param body the body
+ */
+ @UnsupportedAppUsage
+ public void setBody(PduBody body) {
+ mMessageBody = body;
+ }
+
+ /**
+ * Get subject.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getSubject() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Set subject.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setSubject(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Add a "To" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addTo(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-Mms-Priority value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getPriority() {
+ return mPduHeaders.getOctet(PduHeaders.PRIORITY);
+ }
+
+ /**
+ * Set X-Mms-Priority value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setPriority(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.PRIORITY);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value in seconds.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/NotificationInd.java b/telephony/common/com/google/android/mms/pdu/NotificationInd.java
new file mode 100644
index 0000000..b561bd4
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/NotificationInd.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Notification.ind PDU.
+ */
+public class NotificationInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public NotificationInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ NotificationInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Content-Class Value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getContentClass() {
+ return mPduHeaders.getOctet(PduHeaders.CONTENT_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Content-Class Value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setContentClass(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.CONTENT_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Content-Location value.
+ * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
+ * Content-location-value = Uri-value
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentLocation() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_LOCATION);
+ }
+
+ /**
+ * Set X-Mms-Content-Location value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setContentLocation(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_LOCATION);
+ }
+
+ /**
+ * Get X-Mms-Expiry value.
+ *
+ * Expiry-value = Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getExpiry() {
+ return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Set X-Mms-Expiry value.
+ *
+ * @param value the value
+ * @throws RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setExpiry(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Message-Size value.
+ * Message-size-value = Long-integer
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getMessageSize() {
+ return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Set X-Mms-Message-Size value.
+ *
+ * @param value the value
+ * @throws RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setMessageSize(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Get subject.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getSubject() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Set subject.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setSubject(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report Value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report Value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public byte getDistributionIndicator() {return 0x00;}
+ * public void setDistributionIndicator(byte value) {}
+ *
+ * public ElementDescriptorValue getElementDescriptor() {return null;}
+ * public void getElementDescriptor(ElementDescriptorValue value) {}
+ *
+ * public byte getPriority() {return 0x00;}
+ * public void setPriority(byte value) {}
+ *
+ * public byte getRecommendedRetrievalMode() {return 0x00;}
+ * public void setRecommendedRetrievalMode(byte value) {}
+ *
+ * public byte getRecommendedRetrievalModeText() {return 0x00;}
+ * public void setRecommendedRetrievalModeText(byte value) {}
+ *
+ * public byte[] getReplaceId() {return 0x00;}
+ * public void setReplaceId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ *
+ * public byte getStored() {return 0x00;}
+ * public void setStored(byte value) {}
+ */
+}
diff --git a/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java
new file mode 100644
index 0000000..3c70f86
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-NofifyResp.ind PDU.
+ */
+public class NotifyRespInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-NotifyResp.ind pdu.
+ *
+ * @param mmsVersion current version of mms
+ * @param transactionId the transaction-id value
+ * @param status the status value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if transactionId is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public NotifyRespInd(int mmsVersion,
+ byte[] transactionId,
+ int status) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ setStatus(status);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ NotifyRespInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Report-Allowed field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public int getReportAllowed() {
+ return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Report-Allowed field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setReportAllowed(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Status field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.STATUS);
+ }
+
+ /**
+ * GetX-Mms-Status field value.
+ *
+ * @return the X-Mms-Status value
+ */
+ public int getStatus() {
+ return mPduHeaders.getOctet(PduHeaders.STATUS);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/PduBody.java b/telephony/common/com/google/android/mms/pdu/PduBody.java
new file mode 100644
index 0000000..51914e4
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/PduBody.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+public class PduBody {
+ private Vector<PduPart> mParts = null;
+
+ private Map<String, PduPart> mPartMapByContentId = null;
+ private Map<String, PduPart> mPartMapByContentLocation = null;
+ private Map<String, PduPart> mPartMapByName = null;
+ private Map<String, PduPart> mPartMapByFileName = null;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public PduBody() {
+ mParts = new Vector<PduPart>();
+
+ mPartMapByContentId = new HashMap<String, PduPart>();
+ mPartMapByContentLocation = new HashMap<String, PduPart>();
+ mPartMapByName = new HashMap<String, PduPart>();
+ mPartMapByFileName = new HashMap<String, PduPart>();
+ }
+
+ private void putPartToMaps(PduPart part) {
+ // Put part to mPartMapByContentId.
+ byte[] contentId = part.getContentId();
+ if(null != contentId) {
+ mPartMapByContentId.put(new String(contentId), part);
+ }
+
+ // Put part to mPartMapByContentLocation.
+ byte[] contentLocation = part.getContentLocation();
+ if(null != contentLocation) {
+ String clc = new String(contentLocation);
+ mPartMapByContentLocation.put(clc, part);
+ }
+
+ // Put part to mPartMapByName.
+ byte[] name = part.getName();
+ if(null != name) {
+ String clc = new String(name);
+ mPartMapByName.put(clc, part);
+ }
+
+ // Put part to mPartMapByFileName.
+ byte[] fileName = part.getFilename();
+ if(null != fileName) {
+ String clc = new String(fileName);
+ mPartMapByFileName.put(clc, part);
+ }
+ }
+
+ /**
+ * Appends the specified part to the end of this body.
+ *
+ * @param part part to be appended
+ * @return true when success, false when fail
+ * @throws NullPointerException when part is null
+ */
+ @UnsupportedAppUsage
+ public boolean addPart(PduPart part) {
+ if(null == part) {
+ throw new NullPointerException();
+ }
+
+ putPartToMaps(part);
+ return mParts.add(part);
+ }
+
+ /**
+ * Inserts the specified part at the specified position.
+ *
+ * @param index index at which the specified part is to be inserted
+ * @param part part to be inserted
+ * @throws NullPointerException when part is null
+ */
+ @UnsupportedAppUsage
+ public void addPart(int index, PduPart part) {
+ if(null == part) {
+ throw new NullPointerException();
+ }
+
+ putPartToMaps(part);
+ mParts.add(index, part);
+ }
+
+ /**
+ * Removes the part at the specified position.
+ *
+ * @param index index of the part to return
+ * @return part at the specified index
+ */
+ @UnsupportedAppUsage
+ public PduPart removePart(int index) {
+ return mParts.remove(index);
+ }
+
+ /**
+ * Remove all of the parts.
+ */
+ public void removeAll() {
+ mParts.clear();
+ }
+
+ /**
+ * Get the part at the specified position.
+ *
+ * @param index index of the part to return
+ * @return part at the specified index
+ */
+ @UnsupportedAppUsage
+ public PduPart getPart(int index) {
+ return mParts.get(index);
+ }
+
+ /**
+ * Get the index of the specified part.
+ *
+ * @param part the part object
+ * @return index the index of the first occurrence of the part in this body
+ */
+ @UnsupportedAppUsage
+ public int getPartIndex(PduPart part) {
+ return mParts.indexOf(part);
+ }
+
+ /**
+ * Get the number of parts.
+ *
+ * @return the number of parts
+ */
+ @UnsupportedAppUsage
+ public int getPartsNum() {
+ return mParts.size();
+ }
+
+ /**
+ * Get pdu part by content id.
+ *
+ * @param cid the value of content id.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByContentId(String cid) {
+ return mPartMapByContentId.get(cid);
+ }
+
+ /**
+ * Get pdu part by Content-Location. Content-Location of part is
+ * the same as filename and name(param of content-type).
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByContentLocation(String contentLocation) {
+ return mPartMapByContentLocation.get(contentLocation);
+ }
+
+ /**
+ * Get pdu part by name.
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByName(String name) {
+ return mPartMapByName.get(name);
+ }
+
+ /**
+ * Get pdu part by filename.
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByFileName(String filename) {
+ return mPartMapByFileName.get(filename);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/PduComposer.java b/telephony/common/com/google/android/mms/pdu/PduComposer.java
new file mode 100644
index 0000000..e24bf21
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/PduComposer.java
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 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.google.android.mms.pdu;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.text.TextUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class PduComposer {
+ /**
+ * Address type.
+ */
+ static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1;
+ static private final int PDU_EMAIL_ADDRESS_TYPE = 2;
+ static private final int PDU_IPV4_ADDRESS_TYPE = 3;
+ static private final int PDU_IPV6_ADDRESS_TYPE = 4;
+ static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5;
+
+ /**
+ * Address regular expression string.
+ */
+ static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+";
+ static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" +
+ "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}";
+ static final String REGEXP_IPV6_ADDRESS_TYPE =
+ "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
+ "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
+ "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}";
+ static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" +
+ "[0-9]{1,3}\\.{1}[0-9]{1,3}";
+
+ /**
+ * The postfix strings of address.
+ */
+ static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN";
+ static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4";
+ static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6";
+
+ /**
+ * Error values.
+ */
+ static private final int PDU_COMPOSE_SUCCESS = 0;
+ static private final int PDU_COMPOSE_CONTENT_ERROR = 1;
+ static private final int PDU_COMPOSE_FIELD_NOT_SET = 2;
+ static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3;
+
+ /**
+ * WAP values defined in WSP spec.
+ */
+ static private final int QUOTED_STRING_FLAG = 34;
+ static private final int END_STRING_FLAG = 0;
+ static private final int LENGTH_QUOTE = 31;
+ static private final int TEXT_MAX = 127;
+ static private final int SHORT_INTEGER_MAX = 127;
+ static private final int LONG_INTEGER_LENGTH_MAX = 8;
+
+ /**
+ * Block size when read data from InputStream.
+ */
+ static private final int PDU_COMPOSER_BLOCK_SIZE = 1024;
+
+ /**
+ * The output message.
+ */
+ @UnsupportedAppUsage
+ protected ByteArrayOutputStream mMessage = null;
+
+ /**
+ * The PDU.
+ */
+ @UnsupportedAppUsage
+ private GenericPdu mPdu = null;
+
+ /**
+ * Current visiting position of the mMessage.
+ */
+ @UnsupportedAppUsage
+ protected int mPosition = 0;
+
+ /**
+ * Message compose buffer stack.
+ */
+ @UnsupportedAppUsage
+ private BufferStack mStack = null;
+
+ /**
+ * Content resolver.
+ */
+ @UnsupportedAppUsage
+ private final ContentResolver mResolver;
+
+ /**
+ * Header of this pdu.
+ */
+ @UnsupportedAppUsage
+ private PduHeaders mPduHeader = null;
+
+ /**
+ * Map of all content type
+ */
+ @UnsupportedAppUsage
+ private static HashMap<String, Integer> mContentTypeMap = null;
+
+ static {
+ mContentTypeMap = new HashMap<String, Integer>();
+
+ int i;
+ for (i = 0; i < PduContentTypes.contentTypes.length; i++) {
+ mContentTypeMap.put(PduContentTypes.contentTypes[i], i);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context the context
+ * @param pdu the pdu to be composed
+ */
+ @UnsupportedAppUsage
+ public PduComposer(Context context, GenericPdu pdu) {
+ mPdu = pdu;
+ mResolver = context.getContentResolver();
+ mPduHeader = pdu.getPduHeaders();
+ mStack = new BufferStack();
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ /**
+ * Make the message. No need to check whether mandatory fields are set,
+ * because the constructors of outgoing pdus are taking care of this.
+ *
+ * @return OutputStream of maked message. Return null if
+ * the PDU is invalid.
+ */
+ @UnsupportedAppUsage
+ public byte[] make() {
+ // Get Message-type.
+ int type = mPdu.getMessageType();
+
+ /* make the message */
+ switch (type) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ if (makeAckInd() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ default:
+ return null;
+ }
+
+ return mMessage.toByteArray();
+ }
+
+ /**
+ * Copy buf to mMessage.
+ */
+ @UnsupportedAppUsage
+ protected void arraycopy(byte[] buf, int pos, int length) {
+ mMessage.write(buf, pos, length);
+ mPosition = mPosition + length;
+ }
+
+ /**
+ * Append a byte to mMessage.
+ */
+ protected void append(int value) {
+ mMessage.write(value);
+ mPosition ++;
+ }
+
+ /**
+ * Append short integer value to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendShortInteger(int value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Short-integer = OCTET
+ * ; Integers in range 0-127 shall be encoded as a one octet value
+ * ; with the most significant bit set to one (1xxx xxxx) and with
+ * ; the value in the remaining least significant bits.
+ * In our implementation, only low 7 bits are stored and otherwise
+ * bits are ignored.
+ */
+ append((value | 0x80) & 0xff);
+ }
+
+ /**
+ * Append an octet number between 128 and 255 into mMessage.
+ * NOTE:
+ * A value between 0 and 127 should be appended by using appendShortInteger.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendOctet(int number) {
+ append(number);
+ }
+
+ /**
+ * Append a short length into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ protected void appendShortLength(int value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Short-length = <Any octet 0-30>
+ */
+ append(value);
+ }
+
+ /**
+ * Append long integer into mMessage. it's used for really long integers.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendLongInteger(long longInt) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Long-integer = Short-length Multi-octet-integer
+ * ; The Short-length indicates the length of the Multi-octet-integer
+ * Multi-octet-integer = 1*30 OCTET
+ * ; The content octets shall be an unsigned integer value with the
+ * ; most significant octet encoded first (big-endian representation).
+ * ; The minimum number of octets must be used to encode the value.
+ */
+ int size;
+ long temp = longInt;
+
+ // Count the length of the long integer.
+ for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) {
+ temp = (temp >>> 8);
+ }
+
+ // Set Length.
+ appendShortLength(size);
+
+ // Count and set the long integer.
+ int i;
+ int shift = (size -1) * 8;
+
+ for (i = 0; i < size; i++) {
+ append((int)((longInt >>> shift) & 0xff));
+ shift = shift - 8;
+ }
+ }
+
+ /**
+ * Append text string into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendTextString(byte[] text) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Text-string = [Quote] *TEXT End-of-string
+ * ; If the first character in the TEXT is in the range of 128-255,
+ * ; a Quote character must precede it. Otherwise the Quote character
+ * ;must be omitted. The Quote is not part of the contents.
+ */
+ if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255
+ append(TEXT_MAX);
+ }
+
+ arraycopy(text, 0, text.length);
+ append(0);
+ }
+
+ /**
+ * Append text string into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendTextString(String str) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Text-string = [Quote] *TEXT End-of-string
+ * ; If the first character in the TEXT is in the range of 128-255,
+ * ; a Quote character must precede it. Otherwise the Quote character
+ * ;must be omitted. The Quote is not part of the contents.
+ */
+ appendTextString(str.getBytes());
+ }
+
+ /**
+ * Append encoded string value to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendEncodedString(EncodedStringValue enStr) {
+ /*
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C:
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+ assert(enStr != null);
+
+ int charset = enStr.getCharacterSet();
+ byte[] textString = enStr.getTextString();
+ if (null == textString) {
+ return;
+ }
+
+ /*
+ * In the implementation of EncodedStringValue, the charset field will
+ * never be 0. It will always be composed as
+ * Encoded-string-value = Value-length Char-set Text-string
+ */
+ mStack.newbuf();
+ PositionMarker start = mStack.mark();
+
+ appendShortInteger(charset);
+ appendTextString(textString);
+
+ int len = start.getLength();
+ mStack.pop();
+ appendValueLength(len);
+ mStack.copy();
+ }
+
+ /**
+ * Append uintvar integer into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendUintvarInteger(long value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * To encode a large unsigned integer, split it into 7-bit fragments
+ * and place them in the payloads of multiple octets. The most significant
+ * bits are placed in the first octets with the least significant bits
+ * ending up in the last octet. All octets MUST set the Continue bit to 1
+ * except the last octet, which MUST set the Continue bit to 0.
+ */
+ int i;
+ long max = SHORT_INTEGER_MAX;
+
+ for (i = 0; i < 5; i++) {
+ if (value < max) {
+ break;
+ }
+
+ max = (max << 7) | 0x7fl;
+ }
+
+ while(i > 0) {
+ long temp = value >>> (i * 7);
+ temp = temp & 0x7f;
+
+ append((int)((temp | 0x80) & 0xff));
+
+ i--;
+ }
+
+ append((int)(value & 0x7f));
+ }
+
+ /**
+ * Append date value into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ protected void appendDateValue(long date) {
+ /*
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C:
+ * Date-value = Long-integer
+ */
+ appendLongInteger(date);
+ }
+
+ /**
+ * Append value length to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendValueLength(long value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Value-length = Short-length | (Length-quote Length)
+ * ; Value length is used to indicate the length of the value to follow
+ * Short-length = <Any octet 0-30>
+ * Length-quote = <Octet 31>
+ * Length = Uintvar-integer
+ */
+ if (value < LENGTH_QUOTE) {
+ appendShortLength((int) value);
+ return;
+ }
+
+ append(LENGTH_QUOTE);
+ appendUintvarInteger(value);
+ }
+
+ /**
+ * Append quoted string to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendQuotedString(byte[] text) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
+ * ;quotation-marks <"> removed.
+ */
+ append(QUOTED_STRING_FLAG);
+ arraycopy(text, 0, text.length);
+ append(END_STRING_FLAG);
+ }
+
+ /**
+ * Append quoted string to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendQuotedString(String str) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
+ * ;quotation-marks <"> removed.
+ */
+ appendQuotedString(str.getBytes());
+ }
+
+ private EncodedStringValue appendAddressType(EncodedStringValue address) {
+ EncodedStringValue temp = null;
+
+ try {
+ int addressType = checkAddressType(address.getString());
+ temp = EncodedStringValue.copy(address);
+ if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) {
+ // Phone number.
+ temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes());
+ } else if (PDU_IPV4_ADDRESS_TYPE == addressType) {
+ // Ipv4 address.
+ temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes());
+ } else if (PDU_IPV6_ADDRESS_TYPE == addressType) {
+ // Ipv6 address.
+ temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes());
+ }
+ } catch (NullPointerException e) {
+ return null;
+ }
+
+ return temp;
+ }
+
+ /**
+ * Append header to mMessage.
+ */
+ @UnsupportedAppUsage
+ private int appendHeader(int field) {
+ switch (field) {
+ case PduHeaders.MMS_VERSION:
+ appendOctet(field);
+
+ int version = mPduHeader.getOctet(field);
+ if (0 == version) {
+ appendShortInteger(PduHeaders.CURRENT_MMS_VERSION);
+ } else {
+ appendShortInteger(version);
+ }
+
+ break;
+
+ case PduHeaders.MESSAGE_ID:
+ case PduHeaders.TRANSACTION_ID:
+ byte[] textString = mPduHeader.getTextString(field);
+ if (null == textString) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendTextString(textString);
+ break;
+
+ case PduHeaders.TO:
+ case PduHeaders.BCC:
+ case PduHeaders.CC:
+ EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field);
+
+ if (null == addr) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ EncodedStringValue temp;
+ for (int i = 0; i < addr.length; i++) {
+ temp = appendAddressType(addr[i]);
+ if (temp == null) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendOctet(field);
+ appendEncodedString(temp);
+ }
+ break;
+
+ case PduHeaders.FROM:
+ // Value-length (Address-present-token Encoded-string-value | Insert-address-token)
+ appendOctet(field);
+
+ EncodedStringValue from = mPduHeader.getEncodedStringValue(field);
+ if ((from == null)
+ || TextUtils.isEmpty(from.getString())
+ || new String(from.getTextString()).equals(
+ PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) {
+ // Length of from = 1
+ append(1);
+ // Insert-address-token = <Octet 129>
+ append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN);
+ } else {
+ mStack.newbuf();
+ PositionMarker fstart = mStack.mark();
+
+ // Address-present-token = <Octet 128>
+ append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN);
+
+ temp = appendAddressType(from);
+ if (temp == null) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendEncodedString(temp);
+
+ int flen = fstart.getLength();
+ mStack.pop();
+ appendValueLength(flen);
+ mStack.copy();
+ }
+ break;
+
+ case PduHeaders.READ_STATUS:
+ case PduHeaders.STATUS:
+ case PduHeaders.REPORT_ALLOWED:
+ case PduHeaders.PRIORITY:
+ case PduHeaders.DELIVERY_REPORT:
+ case PduHeaders.READ_REPORT:
+ case PduHeaders.RETRIEVE_STATUS:
+ int octet = mPduHeader.getOctet(field);
+ if (0 == octet) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendOctet(octet);
+ break;
+
+ case PduHeaders.DATE:
+ long date = mPduHeader.getLongInteger(field);
+ if (-1 == date) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendDateValue(date);
+ break;
+
+ case PduHeaders.SUBJECT:
+ case PduHeaders.RETRIEVE_TEXT:
+ EncodedStringValue enString =
+ mPduHeader.getEncodedStringValue(field);
+ if (null == enString) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendEncodedString(enString);
+ break;
+
+ case PduHeaders.MESSAGE_CLASS:
+ byte[] messageClass = mPduHeader.getTextString(field);
+ if (null == messageClass) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_AUTO);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL);
+ } else {
+ appendTextString(messageClass);
+ }
+ break;
+
+ case PduHeaders.EXPIRY:
+ long expiry = mPduHeader.getLongInteger(field);
+ if (-1 == expiry) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+
+ mStack.newbuf();
+ PositionMarker expiryStart = mStack.mark();
+
+ append(PduHeaders.VALUE_RELATIVE_TOKEN);
+ appendLongInteger(expiry);
+
+ int expiryLength = expiryStart.getLength();
+ mStack.pop();
+ appendValueLength(expiryLength);
+ mStack.copy();
+ break;
+
+ default:
+ return PDU_COMPOSE_FIELD_NOT_SUPPORTED;
+ }
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make ReadRec.Ind.
+ */
+ private int makeReadRecInd() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Message-ID
+ if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // To
+ if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // From
+ if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Date Optional
+ appendHeader(PduHeaders.DATE);
+
+ // X-Mms-Read-Status
+ if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Applic-ID Optional(not support)
+ // X-Mms-Reply-Applic-ID Optional(not support)
+ // X-Mms-Aux-Applic-Info Optional(not support)
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make NotifyResp.Ind.
+ */
+ private int makeNotifyResp() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
+
+ // X-Mms-Transaction-ID
+ if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Status
+ if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Report-Allowed Optional (not support)
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make Acknowledge.Ind.
+ */
+ private int makeAckInd() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
+
+ // X-Mms-Transaction-ID
+ if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Report-Allowed Optional
+ appendHeader(PduHeaders.REPORT_ALLOWED);
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make Send.req.
+ */
+ private int makeSendRetrievePdu(int type) {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(type);
+
+ // X-Mms-Transaction-ID
+ appendOctet(PduHeaders.TRANSACTION_ID);
+
+ byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID);
+ if (trid == null) {
+ // Transaction-ID should be set(by Transaction) before make().
+ throw new IllegalArgumentException("Transaction-ID is null.");
+ }
+ appendTextString(trid);
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Date Date-value Optional.
+ appendHeader(PduHeaders.DATE);
+
+ // From
+ if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ boolean recipient = false;
+
+ // To
+ if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Cc
+ if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Bcc
+ if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Need at least one of "cc", "bcc" and "to".
+ if (false == recipient) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Subject Optional
+ appendHeader(PduHeaders.SUBJECT);
+
+ // X-Mms-Message-Class Optional
+ // Message-class-value = Class-identifier | Token-text
+ appendHeader(PduHeaders.MESSAGE_CLASS);
+
+ // X-Mms-Expiry Optional
+ appendHeader(PduHeaders.EXPIRY);
+
+ // X-Mms-Priority Optional
+ appendHeader(PduHeaders.PRIORITY);
+
+ // X-Mms-Delivery-Report Optional
+ appendHeader(PduHeaders.DELIVERY_REPORT);
+
+ // X-Mms-Read-Report Optional
+ appendHeader(PduHeaders.READ_REPORT);
+
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ // X-Mms-Retrieve-Status Optional
+ appendHeader(PduHeaders.RETRIEVE_STATUS);
+ // X-Mms-Retrieve-Text Optional
+ appendHeader(PduHeaders.RETRIEVE_TEXT);
+ }
+
+
+ // Content-Type
+ appendOctet(PduHeaders.CONTENT_TYPE);
+
+ // Message body
+ return makeMessageBody(type);
+ }
+
+ /**
+ * Make message body.
+ */
+ private int makeMessageBody(int type) {
+ // 1. add body informations
+ mStack.newbuf(); // Switching buffer because we need to
+
+ PositionMarker ctStart = mStack.mark();
+
+ // This contentTypeIdentifier should be used for type of attachment...
+ String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE));
+ Integer contentTypeIdentifier = mContentTypeMap.get(contentType);
+ if (contentTypeIdentifier == null) {
+ // content type is mandatory
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendShortInteger(contentTypeIdentifier.intValue());
+
+ // content-type parameter: start
+ PduBody body;
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ body = ((RetrieveConf) mPdu).getBody();
+ } else {
+ body = ((SendReq) mPdu).getBody();
+ }
+ if (null == body || body.getPartsNum() == 0) {
+ // empty message
+ appendUintvarInteger(0);
+ mStack.pop();
+ mStack.copy();
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ PduPart part;
+ try {
+ part = body.getPart(0);
+
+ byte[] start = part.getContentId();
+ if (start != null) {
+ appendOctet(PduPart.P_DEP_START);
+ if (('<' == start[0]) && ('>' == start[start.length - 1])) {
+ appendTextString(start);
+ } else {
+ appendTextString("<" + new String(start) + ">");
+ }
+ }
+
+ // content-type parameter: type
+ appendOctet(PduPart.P_CT_MR_TYPE);
+ appendTextString(part.getContentType());
+ }
+ catch (ArrayIndexOutOfBoundsException e){
+ e.printStackTrace();
+ }
+
+ int ctLength = ctStart.getLength();
+ mStack.pop();
+ appendValueLength(ctLength);
+ mStack.copy();
+
+ // 3. add content
+ int partNum = body.getPartsNum();
+ appendUintvarInteger(partNum);
+ for (int i = 0; i < partNum; i++) {
+ part = body.getPart(i);
+ mStack.newbuf(); // Leaving space for header lengh and data length
+ PositionMarker attachment = mStack.mark();
+
+ mStack.newbuf(); // Leaving space for Content-Type length
+ PositionMarker contentTypeBegin = mStack.mark();
+
+ byte[] partContentType = part.getContentType();
+
+ if (partContentType == null) {
+ // content type is mandatory
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // content-type value
+ Integer partContentTypeIdentifier =
+ mContentTypeMap.get(new String(partContentType));
+ if (partContentTypeIdentifier == null) {
+ appendTextString(partContentType);
+ } else {
+ appendShortInteger(partContentTypeIdentifier.intValue());
+ }
+
+ /* Content-type parameter : name.
+ * The value of name, filename, content-location is the same.
+ * Just one of them is enough for this PDU.
+ */
+ byte[] name = part.getName();
+
+ if (null == name) {
+ name = part.getFilename();
+
+ if (null == name) {
+ name = part.getContentLocation();
+
+ if (null == name) {
+ /* at lease one of name, filename, Content-location
+ * should be available.
+ */
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+ }
+ }
+ appendOctet(PduPart.P_DEP_NAME);
+ appendTextString(name);
+
+ // content-type parameter : charset
+ int charset = part.getCharset();
+ if (charset != 0) {
+ appendOctet(PduPart.P_CHARSET);
+ appendShortInteger(charset);
+ }
+
+ int contentTypeLength = contentTypeBegin.getLength();
+ mStack.pop();
+ appendValueLength(contentTypeLength);
+ mStack.copy();
+
+ // content id
+ byte[] contentId = part.getContentId();
+
+ if (null != contentId) {
+ appendOctet(PduPart.P_CONTENT_ID);
+ if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) {
+ appendQuotedString(contentId);
+ } else {
+ appendQuotedString("<" + new String(contentId) + ">");
+ }
+ }
+
+ // content-location
+ byte[] contentLocation = part.getContentLocation();
+ if (null != contentLocation) {
+ appendOctet(PduPart.P_CONTENT_LOCATION);
+ appendTextString(contentLocation);
+ }
+
+ // content
+ int headerLength = attachment.getLength();
+
+ int dataLength = 0; // Just for safety...
+ byte[] partData = part.getData();
+
+ if (partData != null) {
+ arraycopy(partData, 0, partData.length);
+ dataLength = partData.length;
+ } else {
+ InputStream cr = null;
+ try {
+ byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE];
+ cr = mResolver.openInputStream(part.getDataUri());
+ int len = 0;
+ while ((len = cr.read(buffer)) != -1) {
+ mMessage.write(buffer, 0, len);
+ mPosition += len;
+ dataLength += len;
+ }
+ } catch (FileNotFoundException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } catch (IOException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } catch (RuntimeException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } finally {
+ if (cr != null) {
+ try {
+ cr.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ if (dataLength != (attachment.getLength() - headerLength)) {
+ throw new RuntimeException("BUG: Length sanity check failed");
+ }
+
+ mStack.pop();
+ appendUintvarInteger(headerLength);
+ appendUintvarInteger(dataLength);
+ mStack.copy();
+ }
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Record current message informations.
+ */
+ static private class LengthRecordNode {
+ ByteArrayOutputStream currentMessage = null;
+ public int currentPosition = 0;
+
+ public LengthRecordNode next = null;
+ }
+
+ /**
+ * Mark current message position and stact size.
+ */
+ private class PositionMarker {
+ private int c_pos; // Current position
+ private int currentStackSize; // Current stack size
+
+ @UnsupportedAppUsage
+ int getLength() {
+ // If these assert fails, likely that you are finding the
+ // size of buffer that is deep in BufferStack you can only
+ // find the length of the buffer that is on top
+ if (currentStackSize != mStack.stackSize) {
+ throw new RuntimeException("BUG: Invalid call to getLength()");
+ }
+
+ return mPosition - c_pos;
+ }
+ }
+
+ /**
+ * This implementation can be OPTIMIZED to use only
+ * 2 buffers. This optimization involves changing BufferStack
+ * only... Its usage (interface) will not change.
+ */
+ private class BufferStack {
+ private LengthRecordNode stack = null;
+ private LengthRecordNode toCopy = null;
+
+ int stackSize = 0;
+
+ /**
+ * Create a new message buffer and push it into the stack.
+ */
+ @UnsupportedAppUsage
+ void newbuf() {
+ // You can't create a new buff when toCopy != null
+ // That is after calling pop() and before calling copy()
+ // If you do, it is a bug
+ if (toCopy != null) {
+ throw new RuntimeException("BUG: Invalid newbuf() before copy()");
+ }
+
+ LengthRecordNode temp = new LengthRecordNode();
+
+ temp.currentMessage = mMessage;
+ temp.currentPosition = mPosition;
+
+ temp.next = stack;
+ stack = temp;
+
+ stackSize = stackSize + 1;
+
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ /**
+ * Pop the message before and record current message in the stack.
+ */
+ @UnsupportedAppUsage
+ void pop() {
+ ByteArrayOutputStream currentMessage = mMessage;
+ int currentPosition = mPosition;
+
+ mMessage = stack.currentMessage;
+ mPosition = stack.currentPosition;
+
+ toCopy = stack;
+ // Re using the top element of the stack to avoid memory allocation
+
+ stack = stack.next;
+ stackSize = stackSize - 1;
+
+ toCopy.currentMessage = currentMessage;
+ toCopy.currentPosition = currentPosition;
+ }
+
+ /**
+ * Append current message to the message before.
+ */
+ @UnsupportedAppUsage
+ void copy() {
+ arraycopy(toCopy.currentMessage.toByteArray(), 0,
+ toCopy.currentPosition);
+
+ toCopy = null;
+ }
+
+ /**
+ * Mark current message position
+ */
+ @UnsupportedAppUsage
+ PositionMarker mark() {
+ PositionMarker m = new PositionMarker();
+
+ m.c_pos = mPosition;
+ m.currentStackSize = stackSize;
+
+ return m;
+ }
+ }
+
+ /**
+ * Check address type.
+ *
+ * @param address address string without the postfix stinng type,
+ * such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4"
+ * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number,
+ * PDU_EMAIL_ADDRESS_TYPE if it is email address,
+ * PDU_IPV4_ADDRESS_TYPE if it is ipv4 address,
+ * PDU_IPV6_ADDRESS_TYPE if it is ipv6 address,
+ * PDU_UNKNOWN_ADDRESS_TYPE if it is unknown.
+ */
+ protected static int checkAddressType(String address) {
+ /**
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8.
+ * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode)
+ * e-mail = mailbox; to the definition of mailbox as described in
+ * section 3.4 of [RFC2822], but excluding the
+ * obsolete definitions as indicated by the "obs-" prefix.
+ * device-address = ( global-phone-number "/TYPE=PLMN" )
+ * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" )
+ * / ( escaped-value "/TYPE=" address-type )
+ *
+ * global-phone-number = ["+"] 1*( DIGIT / written-sep )
+ * written-sep =("-"/".")
+ *
+ * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value
+ *
+ * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373
+ */
+
+ if (null == address) {
+ return PDU_UNKNOWN_ADDRESS_TYPE;
+ }
+
+ if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) {
+ // Ipv4 address.
+ return PDU_IPV4_ADDRESS_TYPE;
+ }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) {
+ // Phone number.
+ return PDU_PHONE_NUMBER_ADDRESS_TYPE;
+ } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) {
+ // Email address.
+ return PDU_EMAIL_ADDRESS_TYPE;
+ } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) {
+ // Ipv6 address.
+ return PDU_IPV6_ADDRESS_TYPE;
+ } else {
+ // Unknown address.
+ return PDU_UNKNOWN_ADDRESS_TYPE;
+ }
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/PduContentTypes.java b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java
new file mode 100644
index 0000000..8551b2f
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class PduContentTypes {
+ /**
+ * All content types. From:
+ * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.htm
+ */
+ @UnsupportedAppUsage
+ static final String[] contentTypes = {
+ "*/*", /* 0x00 */
+ "text/*", /* 0x01 */
+ "text/html", /* 0x02 */
+ "text/plain", /* 0x03 */
+ "text/x-hdml", /* 0x04 */
+ "text/x-ttml", /* 0x05 */
+ "text/x-vCalendar", /* 0x06 */
+ "text/x-vCard", /* 0x07 */
+ "text/vnd.wap.wml", /* 0x08 */
+ "text/vnd.wap.wmlscript", /* 0x09 */
+ "text/vnd.wap.wta-event", /* 0x0A */
+ "multipart/*", /* 0x0B */
+ "multipart/mixed", /* 0x0C */
+ "multipart/form-data", /* 0x0D */
+ "multipart/byterantes", /* 0x0E */
+ "multipart/alternative", /* 0x0F */
+ "application/*", /* 0x10 */
+ "application/java-vm", /* 0x11 */
+ "application/x-www-form-urlencoded", /* 0x12 */
+ "application/x-hdmlc", /* 0x13 */
+ "application/vnd.wap.wmlc", /* 0x14 */
+ "application/vnd.wap.wmlscriptc", /* 0x15 */
+ "application/vnd.wap.wta-eventc", /* 0x16 */
+ "application/vnd.wap.uaprof", /* 0x17 */
+ "application/vnd.wap.wtls-ca-certificate", /* 0x18 */
+ "application/vnd.wap.wtls-user-certificate", /* 0x19 */
+ "application/x-x509-ca-cert", /* 0x1A */
+ "application/x-x509-user-cert", /* 0x1B */
+ "image/*", /* 0x1C */
+ "image/gif", /* 0x1D */
+ "image/jpeg", /* 0x1E */
+ "image/tiff", /* 0x1F */
+ "image/png", /* 0x20 */
+ "image/vnd.wap.wbmp", /* 0x21 */
+ "application/vnd.wap.multipart.*", /* 0x22 */
+ "application/vnd.wap.multipart.mixed", /* 0x23 */
+ "application/vnd.wap.multipart.form-data", /* 0x24 */
+ "application/vnd.wap.multipart.byteranges", /* 0x25 */
+ "application/vnd.wap.multipart.alternative", /* 0x26 */
+ "application/xml", /* 0x27 */
+ "text/xml", /* 0x28 */
+ "application/vnd.wap.wbxml", /* 0x29 */
+ "application/x-x968-cross-cert", /* 0x2A */
+ "application/x-x968-ca-cert", /* 0x2B */
+ "application/x-x968-user-cert", /* 0x2C */
+ "text/vnd.wap.si", /* 0x2D */
+ "application/vnd.wap.sic", /* 0x2E */
+ "text/vnd.wap.sl", /* 0x2F */
+ "application/vnd.wap.slc", /* 0x30 */
+ "text/vnd.wap.co", /* 0x31 */
+ "application/vnd.wap.coc", /* 0x32 */
+ "application/vnd.wap.multipart.related", /* 0x33 */
+ "application/vnd.wap.sia", /* 0x34 */
+ "text/vnd.wap.connectivity-xml", /* 0x35 */
+ "application/vnd.wap.connectivity-wbxml", /* 0x36 */
+ "application/pkcs7-mime", /* 0x37 */
+ "application/vnd.wap.hashed-certificate", /* 0x38 */
+ "application/vnd.wap.signed-certificate", /* 0x39 */
+ "application/vnd.wap.cert-response", /* 0x3A */
+ "application/xhtml+xml", /* 0x3B */
+ "application/wml+xml", /* 0x3C */
+ "text/css", /* 0x3D */
+ "application/vnd.wap.mms-message", /* 0x3E */
+ "application/vnd.wap.rollover-certificate", /* 0x3F */
+ "application/vnd.wap.locc+wbxml", /* 0x40 */
+ "application/vnd.wap.loc+xml", /* 0x41 */
+ "application/vnd.syncml.dm+wbxml", /* 0x42 */
+ "application/vnd.syncml.dm+xml", /* 0x43 */
+ "application/vnd.syncml.notification", /* 0x44 */
+ "application/vnd.wap.xhtml+xml", /* 0x45 */
+ "application/vnd.wv.csp.cir", /* 0x46 */
+ "application/vnd.oma.dd+xml", /* 0x47 */
+ "application/vnd.oma.drm.message", /* 0x48 */
+ "application/vnd.oma.drm.content", /* 0x49 */
+ "application/vnd.oma.drm.rights+xml", /* 0x4A */
+ "application/vnd.oma.drm.rights+wbxml", /* 0x4B */
+ "application/vnd.wv.csp+xml", /* 0x4C */
+ "application/vnd.wv.csp+wbxml", /* 0x4D */
+ "application/vnd.syncml.ds.notification", /* 0x4E */
+ "audio/*", /* 0x4F */
+ "video/*", /* 0x50 */
+ "application/vnd.oma.dd2+xml", /* 0x51 */
+ "application/mikey" /* 0x52 */
+ };
+}
diff --git a/telephony/common/com/google/android/mms/pdu/PduHeaders.java b/telephony/common/com/google/android/mms/pdu/PduHeaders.java
new file mode 100644
index 0000000..b524464
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/PduHeaders.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class PduHeaders {
+ /**
+ * All pdu header fields.
+ */
+ public static final int BCC = 0x81;
+ public static final int CC = 0x82;
+ public static final int CONTENT_LOCATION = 0x83;
+ public static final int CONTENT_TYPE = 0x84;
+ public static final int DATE = 0x85;
+ public static final int DELIVERY_REPORT = 0x86;
+ public static final int DELIVERY_TIME = 0x87;
+ public static final int EXPIRY = 0x88;
+ public static final int FROM = 0x89;
+ public static final int MESSAGE_CLASS = 0x8A;
+ public static final int MESSAGE_ID = 0x8B;
+ public static final int MESSAGE_TYPE = 0x8C;
+ public static final int MMS_VERSION = 0x8D;
+ public static final int MESSAGE_SIZE = 0x8E;
+ public static final int PRIORITY = 0x8F;
+
+ public static final int READ_REPLY = 0x90;
+ public static final int READ_REPORT = 0x90;
+ public static final int REPORT_ALLOWED = 0x91;
+ public static final int RESPONSE_STATUS = 0x92;
+ public static final int RESPONSE_TEXT = 0x93;
+ public static final int SENDER_VISIBILITY = 0x94;
+ public static final int STATUS = 0x95;
+ public static final int SUBJECT = 0x96;
+ public static final int TO = 0x97;
+ public static final int TRANSACTION_ID = 0x98;
+ public static final int RETRIEVE_STATUS = 0x99;
+ public static final int RETRIEVE_TEXT = 0x9A;
+ public static final int READ_STATUS = 0x9B;
+ public static final int REPLY_CHARGING = 0x9C;
+ public static final int REPLY_CHARGING_DEADLINE = 0x9D;
+ public static final int REPLY_CHARGING_ID = 0x9E;
+ public static final int REPLY_CHARGING_SIZE = 0x9F;
+
+ public static final int PREVIOUSLY_SENT_BY = 0xA0;
+ public static final int PREVIOUSLY_SENT_DATE = 0xA1;
+ public static final int STORE = 0xA2;
+ public static final int MM_STATE = 0xA3;
+ public static final int MM_FLAGS = 0xA4;
+ public static final int STORE_STATUS = 0xA5;
+ public static final int STORE_STATUS_TEXT = 0xA6;
+ public static final int STORED = 0xA7;
+ public static final int ATTRIBUTES = 0xA8;
+ public static final int TOTALS = 0xA9;
+ public static final int MBOX_TOTALS = 0xAA;
+ public static final int QUOTAS = 0xAB;
+ public static final int MBOX_QUOTAS = 0xAC;
+ public static final int MESSAGE_COUNT = 0xAD;
+ public static final int CONTENT = 0xAE;
+ public static final int START = 0xAF;
+
+ public static final int ADDITIONAL_HEADERS = 0xB0;
+ public static final int DISTRIBUTION_INDICATOR = 0xB1;
+ public static final int ELEMENT_DESCRIPTOR = 0xB2;
+ public static final int LIMIT = 0xB3;
+ public static final int RECOMMENDED_RETRIEVAL_MODE = 0xB4;
+ public static final int RECOMMENDED_RETRIEVAL_MODE_TEXT = 0xB5;
+ public static final int STATUS_TEXT = 0xB6;
+ public static final int APPLIC_ID = 0xB7;
+ public static final int REPLY_APPLIC_ID = 0xB8;
+ public static final int AUX_APPLIC_ID = 0xB9;
+ public static final int CONTENT_CLASS = 0xBA;
+ public static final int DRM_CONTENT = 0xBB;
+ public static final int ADAPTATION_ALLOWED = 0xBC;
+ public static final int REPLACE_ID = 0xBD;
+ public static final int CANCEL_ID = 0xBE;
+ public static final int CANCEL_STATUS = 0xBF;
+
+ /**
+ * X-Mms-Message-Type field types.
+ */
+ public static final int MESSAGE_TYPE_SEND_REQ = 0x80;
+ public static final int MESSAGE_TYPE_SEND_CONF = 0x81;
+ public static final int MESSAGE_TYPE_NOTIFICATION_IND = 0x82;
+ public static final int MESSAGE_TYPE_NOTIFYRESP_IND = 0x83;
+ public static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
+ public static final int MESSAGE_TYPE_ACKNOWLEDGE_IND = 0x85;
+ public static final int MESSAGE_TYPE_DELIVERY_IND = 0x86;
+ public static final int MESSAGE_TYPE_READ_REC_IND = 0x87;
+ public static final int MESSAGE_TYPE_READ_ORIG_IND = 0x88;
+ public static final int MESSAGE_TYPE_FORWARD_REQ = 0x89;
+ public static final int MESSAGE_TYPE_FORWARD_CONF = 0x8A;
+ public static final int MESSAGE_TYPE_MBOX_STORE_REQ = 0x8B;
+ public static final int MESSAGE_TYPE_MBOX_STORE_CONF = 0x8C;
+ public static final int MESSAGE_TYPE_MBOX_VIEW_REQ = 0x8D;
+ public static final int MESSAGE_TYPE_MBOX_VIEW_CONF = 0x8E;
+ public static final int MESSAGE_TYPE_MBOX_UPLOAD_REQ = 0x8F;
+ public static final int MESSAGE_TYPE_MBOX_UPLOAD_CONF = 0x90;
+ public static final int MESSAGE_TYPE_MBOX_DELETE_REQ = 0x91;
+ public static final int MESSAGE_TYPE_MBOX_DELETE_CONF = 0x92;
+ public static final int MESSAGE_TYPE_MBOX_DESCR = 0x93;
+ public static final int MESSAGE_TYPE_DELETE_REQ = 0x94;
+ public static final int MESSAGE_TYPE_DELETE_CONF = 0x95;
+ public static final int MESSAGE_TYPE_CANCEL_REQ = 0x96;
+ public static final int MESSAGE_TYPE_CANCEL_CONF = 0x97;
+
+ /**
+ * X-Mms-Delivery-Report |
+ * X-Mms-Read-Report |
+ * X-Mms-Report-Allowed |
+ * X-Mms-Sender-Visibility |
+ * X-Mms-Store |
+ * X-Mms-Stored |
+ * X-Mms-Totals |
+ * X-Mms-Quotas |
+ * X-Mms-Distribution-Indicator |
+ * X-Mms-DRM-Content |
+ * X-Mms-Adaptation-Allowed |
+ * field types.
+ */
+ public static final int VALUE_YES = 0x80;
+ public static final int VALUE_NO = 0x81;
+
+ /**
+ * Delivery-Time |
+ * Expiry and Reply-Charging-Deadline |
+ * field type components.
+ */
+ public static final int VALUE_ABSOLUTE_TOKEN = 0x80;
+ public static final int VALUE_RELATIVE_TOKEN = 0x81;
+
+ /**
+ * X-Mms-MMS-Version field types.
+ */
+ public static final int MMS_VERSION_1_3 = ((1 << 4) | 3);
+ public static final int MMS_VERSION_1_2 = ((1 << 4) | 2);
+ public static final int MMS_VERSION_1_1 = ((1 << 4) | 1);
+ public static final int MMS_VERSION_1_0 = ((1 << 4) | 0);
+
+ // Current version is 1.2.
+ public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2;
+
+ /**
+ * From field type components.
+ */
+ public static final int FROM_ADDRESS_PRESENT_TOKEN = 0x80;
+ public static final int FROM_INSERT_ADDRESS_TOKEN = 0x81;
+
+ public static final String FROM_ADDRESS_PRESENT_TOKEN_STR = "address-present-token";
+ public static final String FROM_INSERT_ADDRESS_TOKEN_STR = "insert-address-token";
+
+ /**
+ * X-Mms-Status Field.
+ */
+ public static final int STATUS_EXPIRED = 0x80;
+ public static final int STATUS_RETRIEVED = 0x81;
+ public static final int STATUS_REJECTED = 0x82;
+ public static final int STATUS_DEFERRED = 0x83;
+ public static final int STATUS_UNRECOGNIZED = 0x84;
+ public static final int STATUS_INDETERMINATE = 0x85;
+ public static final int STATUS_FORWARDED = 0x86;
+ public static final int STATUS_UNREACHABLE = 0x87;
+
+ /**
+ * MM-Flags field type components.
+ */
+ public static final int MM_FLAGS_ADD_TOKEN = 0x80;
+ public static final int MM_FLAGS_REMOVE_TOKEN = 0x81;
+ public static final int MM_FLAGS_FILTER_TOKEN = 0x82;
+
+ /**
+ * X-Mms-Message-Class field types.
+ */
+ public static final int MESSAGE_CLASS_PERSONAL = 0x80;
+ public static final int MESSAGE_CLASS_ADVERTISEMENT = 0x81;
+ public static final int MESSAGE_CLASS_INFORMATIONAL = 0x82;
+ public static final int MESSAGE_CLASS_AUTO = 0x83;
+
+ public static final String MESSAGE_CLASS_PERSONAL_STR = "personal";
+ public static final String MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement";
+ public static final String MESSAGE_CLASS_INFORMATIONAL_STR = "informational";
+ public static final String MESSAGE_CLASS_AUTO_STR = "auto";
+
+ /**
+ * X-Mms-Priority field types.
+ */
+ public static final int PRIORITY_LOW = 0x80;
+ public static final int PRIORITY_NORMAL = 0x81;
+ public static final int PRIORITY_HIGH = 0x82;
+
+ /**
+ * X-Mms-Response-Status field types.
+ */
+ public static final int RESPONSE_STATUS_OK = 0x80;
+ public static final int RESPONSE_STATUS_ERROR_UNSPECIFIED = 0x81;
+ public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82;
+
+ public static final int RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT = 0x83;
+ public static final int RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED = 0x84;
+
+ public static final int RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND = 0x85;
+ public static final int RESPONSE_STATUS_ERROR_NETWORK_PROBLEM = 0x86;
+ public static final int RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED = 0x87;
+ public static final int RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE = 0x88;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED = 0xC1;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC2;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC3;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS = 0xC4;
+
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED = 0xE3;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE4;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED = 0xE5;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET = 0xE6;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED = 0xE6;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED = 0xE8;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED = 0xE9;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED = 0xEA;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID = 0xEB;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_END = 0xFF;
+
+ /**
+ * X-Mms-Retrieve-Status field types.
+ */
+ public static final int RETRIEVE_STATUS_OK = 0x80;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC1;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC2;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE2;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED = 0xE3;
+ public static final int RETRIEVE_STATUS_ERROR_END = 0xFF;
+
+ /**
+ * X-Mms-Sender-Visibility field types.
+ */
+ public static final int SENDER_VISIBILITY_HIDE = 0x80;
+ public static final int SENDER_VISIBILITY_SHOW = 0x81;
+
+ /**
+ * X-Mms-Read-Status field types.
+ */
+ public static final int READ_STATUS_READ = 0x80;
+ public static final int READ_STATUS__DELETED_WITHOUT_BEING_READ = 0x81;
+
+ /**
+ * X-Mms-Cancel-Status field types.
+ */
+ public static final int CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED = 0x80;
+ public static final int CANCEL_STATUS_REQUEST_CORRUPTED = 0x81;
+
+ /**
+ * X-Mms-Reply-Charging field types.
+ */
+ public static final int REPLY_CHARGING_REQUESTED = 0x80;
+ public static final int REPLY_CHARGING_REQUESTED_TEXT_ONLY = 0x81;
+ public static final int REPLY_CHARGING_ACCEPTED = 0x82;
+ public static final int REPLY_CHARGING_ACCEPTED_TEXT_ONLY = 0x83;
+
+ /**
+ * X-Mms-MM-State field types.
+ */
+ public static final int MM_STATE_DRAFT = 0x80;
+ public static final int MM_STATE_SENT = 0x81;
+ public static final int MM_STATE_NEW = 0x82;
+ public static final int MM_STATE_RETRIEVED = 0x83;
+ public static final int MM_STATE_FORWARDED = 0x84;
+
+ /**
+ * X-Mms-Recommended-Retrieval-Mode field types.
+ */
+ public static final int RECOMMENDED_RETRIEVAL_MODE_MANUAL = 0x80;
+
+ /**
+ * X-Mms-Content-Class field types.
+ */
+ public static final int CONTENT_CLASS_TEXT = 0x80;
+ public static final int CONTENT_CLASS_IMAGE_BASIC = 0x81;
+ public static final int CONTENT_CLASS_IMAGE_RICH = 0x82;
+ public static final int CONTENT_CLASS_VIDEO_BASIC = 0x83;
+ public static final int CONTENT_CLASS_VIDEO_RICH = 0x84;
+ public static final int CONTENT_CLASS_MEGAPIXEL = 0x85;
+ public static final int CONTENT_CLASS_CONTENT_BASIC = 0x86;
+ public static final int CONTENT_CLASS_CONTENT_RICH = 0x87;
+
+ /**
+ * X-Mms-Store-Status field types.
+ */
+ public static final int STORE_STATUS_SUCCESS = 0x80;
+ public static final int STORE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+ public static final int STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC1;
+ public static final int STORE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int STORE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE3;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL = 0xE4;
+ public static final int STORE_STATUS_ERROR_END = 0xFF;
+
+ /**
+ * The map contains the value of all headers.
+ */
+ private HashMap<Integer, Object> mHeaderMap = null;
+
+ /**
+ * Constructor of PduHeaders.
+ */
+ @UnsupportedAppUsage
+ public PduHeaders() {
+ mHeaderMap = new HashMap<Integer, Object>();
+ }
+
+ /**
+ * Get octet value by header field.
+ *
+ * @param field the field
+ * @return the octet value of the pdu header
+ * with specified header field. Return 0 if
+ * the value is not set.
+ */
+ @UnsupportedAppUsage
+ protected int getOctet(int field) {
+ Integer octet = (Integer) mHeaderMap.get(field);
+ if (null == octet) {
+ return 0;
+ }
+
+ return octet;
+ }
+
+ /**
+ * Set octet value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ protected void setOctet(int value, int field)
+ throws InvalidHeaderValueException{
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ switch (field) {
+ case REPORT_ALLOWED:
+ case ADAPTATION_ALLOWED:
+ case DELIVERY_REPORT:
+ case DRM_CONTENT:
+ case DISTRIBUTION_INDICATOR:
+ case QUOTAS:
+ case READ_REPORT:
+ case STORE:
+ case STORED:
+ case TOTALS:
+ case SENDER_VISIBILITY:
+ if ((VALUE_YES != value) && (VALUE_NO != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case READ_STATUS:
+ if ((READ_STATUS_READ != value) &&
+ (READ_STATUS__DELETED_WITHOUT_BEING_READ != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case CANCEL_STATUS:
+ if ((CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED != value) &&
+ (CANCEL_STATUS_REQUEST_CORRUPTED != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case PRIORITY:
+ if ((value < PRIORITY_LOW) || (value > PRIORITY_HIGH)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case STATUS:
+ if ((value < STATUS_EXPIRED) || (value > STATUS_UNREACHABLE)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case REPLY_CHARGING:
+ if ((value < REPLY_CHARGING_REQUESTED)
+ || (value > REPLY_CHARGING_ACCEPTED_TEXT_ONLY)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case MM_STATE:
+ if ((value < MM_STATE_DRAFT) || (value > MM_STATE_FORWARDED)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case RECOMMENDED_RETRIEVAL_MODE:
+ if (RECOMMENDED_RETRIEVAL_MODE_MANUAL != value) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case CONTENT_CLASS:
+ if ((value < CONTENT_CLASS_TEXT)
+ || (value > CONTENT_CLASS_CONTENT_RICH)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case RETRIEVE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.50, we modify the invalid value.
+ if ((value > RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
+ (value < RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if ((value > RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED) &&
+ (value <= RETRIEVE_STATUS_ERROR_END)) {
+ value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
+ } else if ((value < RETRIEVE_STATUS_OK) ||
+ ((value > RETRIEVE_STATUS_OK) &&
+ (value < RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > RETRIEVE_STATUS_ERROR_END)) {
+ value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case STORE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.58, we modify the invalid value.
+ if ((value > STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
+ (value < STORE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = STORE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if ((value > STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL) &&
+ (value <= STORE_STATUS_ERROR_END)) {
+ value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
+ } else if ((value < STORE_STATUS_SUCCESS) ||
+ ((value > STORE_STATUS_SUCCESS) &&
+ (value < STORE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > STORE_STATUS_ERROR_END)) {
+ value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case RESPONSE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.48, we modify the invalid value.
+ if ((value > RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) &&
+ (value < RESPONSE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if (((value > RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID) &&
+ (value <= RESPONSE_STATUS_ERROR_PERMANENT_END)) ||
+ (value < RESPONSE_STATUS_OK) ||
+ ((value > RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE) &&
+ (value < RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > RESPONSE_STATUS_ERROR_PERMANENT_END)) {
+ value = RESPONSE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case MMS_VERSION:
+ if ((value < MMS_VERSION_1_0)|| (value > MMS_VERSION_1_3)) {
+ value = CURRENT_MMS_VERSION; // Current version is the default value.
+ }
+ break;
+ case MESSAGE_TYPE:
+ if ((value < MESSAGE_TYPE_SEND_REQ) || (value > MESSAGE_TYPE_CANCEL_CONF)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ default:
+ // This header value should not be Octect.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Get TextString value by header field.
+ *
+ * @param field the field
+ * @return the TextString value of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected byte[] getTextString(int field) {
+ return (byte[]) mHeaderMap.get(field);
+ }
+
+ /**
+ * Set TextString value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the TextString value of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ protected void setTextString(byte[] value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case TRANSACTION_ID:
+ case REPLY_CHARGING_ID:
+ case AUX_APPLIC_ID:
+ case APPLIC_ID:
+ case REPLY_APPLIC_ID:
+ case MESSAGE_ID:
+ case REPLACE_ID:
+ case CANCEL_ID:
+ case CONTENT_LOCATION:
+ case MESSAGE_CLASS:
+ case CONTENT_TYPE:
+ break;
+ default:
+ // This header value should not be Text-String.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Get EncodedStringValue value by header field.
+ *
+ * @param field the field
+ * @return the EncodedStringValue value of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected EncodedStringValue getEncodedStringValue(int field) {
+ return (EncodedStringValue) mHeaderMap.get(field);
+ }
+
+ /**
+ * Get TO, CC or BCC header value.
+ *
+ * @param field the field
+ * @return the EncodeStringValue array of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected EncodedStringValue[] getEncodedStringValues(int field) {
+ ArrayList<EncodedStringValue> list =
+ (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
+ if (null == list) {
+ return null;
+ }
+ EncodedStringValue[] values = new EncodedStringValue[list.size()];
+ return list.toArray(values);
+ }
+
+ /**
+ * Set EncodedStringValue value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the EncodedStringValue value of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ protected void setEncodedStringValue(EncodedStringValue value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case SUBJECT:
+ case RECOMMENDED_RETRIEVAL_MODE_TEXT:
+ case RETRIEVE_TEXT:
+ case STATUS_TEXT:
+ case STORE_STATUS_TEXT:
+ case RESPONSE_TEXT:
+ case FROM:
+ case PREVIOUSLY_SENT_BY:
+ case MM_FLAGS:
+ break;
+ default:
+ // This header value should not be Encoded-String-Value.
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Set TO, CC or BCC header value.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the EncodedStringValue value array of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ protected void setEncodedStringValues(EncodedStringValue[] value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case BCC:
+ case CC:
+ case TO:
+ break;
+ default:
+ // This header value should not be Encoded-String-Value.
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
+ for (int i = 0; i < value.length; i++) {
+ list.add(value[i]);
+ }
+ mHeaderMap.put(field, list);
+ }
+
+ /**
+ * Append one EncodedStringValue to another.
+ *
+ * @param value the EncodedStringValue to append
+ * @param field the field
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ protected void appendEncodedStringValue(EncodedStringValue value,
+ int field) {
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case BCC:
+ case CC:
+ case TO:
+ break;
+ default:
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ ArrayList<EncodedStringValue> list =
+ (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
+ if (null == list) {
+ list = new ArrayList<EncodedStringValue>();
+ }
+ list.add(value);
+ mHeaderMap.put(field, list);
+ }
+
+ /**
+ * Get LongInteger value by header field.
+ *
+ * @param field the field
+ * @return the LongInteger value of the pdu header
+ * with specified header field. if return -1, the
+ * field is not existed in pdu header.
+ */
+ @UnsupportedAppUsage
+ protected long getLongInteger(int field) {
+ Long longInteger = (Long) mHeaderMap.get(field);
+ if (null == longInteger) {
+ return -1;
+ }
+
+ return longInteger.longValue();
+ }
+
+ /**
+ * Set LongInteger value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ */
+ @UnsupportedAppUsage
+ protected void setLongInteger(long value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ switch (field) {
+ case DATE:
+ case REPLY_CHARGING_SIZE:
+ case MESSAGE_SIZE:
+ case MESSAGE_COUNT:
+ case START:
+ case LIMIT:
+ case DELIVERY_TIME:
+ case EXPIRY:
+ case REPLY_CHARGING_DEADLINE:
+ case PREVIOUSLY_SENT_DATE:
+ break;
+ default:
+ // This header value should not be LongInteger.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java
new file mode 100755
index 0000000..f483994
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/PduParser.java
@@ -0,0 +1,2023 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 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.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class PduParser {
+ /**
+ * The next are WAP values defined in WSP specification.
+ */
+ private static final int QUOTE = 127;
+ private static final int LENGTH_QUOTE = 31;
+ private static final int TEXT_MIN = 32;
+ private static final int TEXT_MAX = 127;
+ private static final int SHORT_INTEGER_MAX = 127;
+ private static final int SHORT_LENGTH_MAX = 30;
+ private static final int LONG_INTEGER_LENGTH_MAX = 8;
+ private static final int QUOTED_STRING_FLAG = 34;
+ private static final int END_STRING_FLAG = 0x00;
+ //The next two are used by the interface "parseWapString" to
+ //distinguish Text-String and Quoted-String.
+ private static final int TYPE_TEXT_STRING = 0;
+ private static final int TYPE_QUOTED_STRING = 1;
+ private static final int TYPE_TOKEN_STRING = 2;
+
+ /**
+ * Specify the part position.
+ */
+ private static final int THE_FIRST_PART = 0;
+ private static final int THE_LAST_PART = 1;
+
+ /**
+ * The pdu data.
+ */
+ private ByteArrayInputStream mPduDataStream = null;
+
+ /**
+ * Store pdu headers
+ */
+ private PduHeaders mHeaders = null;
+
+ /**
+ * Store pdu parts.
+ */
+ private PduBody mBody = null;
+
+ /**
+ * Store the "type" parameter in "Content-Type" header field.
+ */
+ private static byte[] mTypeParam = null;
+
+ /**
+ * Store the "start" parameter in "Content-Type" header field.
+ */
+ private static byte[] mStartParam = null;
+
+ /**
+ * The log tag.
+ */
+ private static final String LOG_TAG = "PduParser";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * Whether to parse content-disposition part header
+ */
+ private final boolean mParseContentDisposition;
+
+ /**
+ * Constructor.
+ *
+ * @param pduDataStream pdu data to be parsed
+ * @param parseContentDisposition whether to parse the Content-Disposition part header
+ */
+ @UnsupportedAppUsage
+ public PduParser(byte[] pduDataStream, boolean parseContentDisposition) {
+ mPduDataStream = new ByteArrayInputStream(pduDataStream);
+ mParseContentDisposition = parseContentDisposition;
+ }
+
+ /**
+ * Parse the pdu.
+ *
+ * @return the pdu structure if parsing successfully.
+ * null if parsing error happened or mandatory fields are not set.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu parse(){
+ if (mPduDataStream == null) {
+ return null;
+ }
+
+ /* parse headers */
+ mHeaders = parseHeaders(mPduDataStream);
+ if (null == mHeaders) {
+ // Parse headers failed.
+ return null;
+ }
+
+ /* get the message type */
+ int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
+
+ /* check mandatory header fields */
+ if (false == checkMandatoryHeader(mHeaders)) {
+ log("check mandatory headers failed!");
+ return null;
+ }
+
+ if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) ||
+ (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) {
+ /* need to parse the parts */
+ mBody = parseParts(mPduDataStream);
+ if (null == mBody) {
+ // Parse parts failed.
+ return null;
+ }
+ }
+
+ switch (messageType) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_REQ");
+ }
+ SendReq sendReq = new SendReq(mHeaders, mBody);
+ return sendReq;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_CONF");
+ }
+ SendConf sendConf = new SendConf(mHeaders);
+ return sendConf;
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFICATION_IND");
+ }
+ NotificationInd notificationInd =
+ new NotificationInd(mHeaders);
+ return notificationInd;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFYRESP_IND");
+ }
+ NotifyRespInd notifyRespInd =
+ new NotifyRespInd(mHeaders);
+ return notifyRespInd;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_RETRIEVE_CONF");
+ }
+ RetrieveConf retrieveConf =
+ new RetrieveConf(mHeaders, mBody);
+
+ byte[] contentType = retrieveConf.getContentType();
+ if (null == contentType) {
+ return null;
+ }
+ String ctTypeStr = new String(contentType);
+ if (ctTypeStr.equals(ContentType.MULTIPART_MIXED)
+ || ctTypeStr.equals(ContentType.MULTIPART_RELATED)
+ || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
+ // The MMS content type must be "application/vnd.wap.multipart.mixed"
+ // or "application/vnd.wap.multipart.related"
+ // or "application/vnd.wap.multipart.alternative"
+ return retrieveConf;
+ } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
+ // "application/vnd.wap.multipart.alternative"
+ // should take only the first part.
+ PduPart firstPart = mBody.getPart(0);
+ mBody.removeAll();
+ mBody.addPart(0, firstPart);
+ return retrieveConf;
+ }
+ return null;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_DELIVERY_IND");
+ }
+ DeliveryInd deliveryInd =
+ new DeliveryInd(mHeaders);
+ return deliveryInd;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_ACKNOWLEDGE_IND");
+ }
+ AcknowledgeInd acknowledgeInd =
+ new AcknowledgeInd(mHeaders);
+ return acknowledgeInd;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_ORIG_IND");
+ }
+ ReadOrigInd readOrigInd =
+ new ReadOrigInd(mHeaders);
+ return readOrigInd;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_REC_IND");
+ }
+ ReadRecInd readRecInd =
+ new ReadRecInd(mHeaders);
+ return readRecInd;
+ default:
+ log("Parser doesn't support this message type in this version!");
+ return null;
+ }
+ }
+
+ /**
+ * Parse pdu headers.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return headers in PduHeaders structure, null when parse fail
+ */
+ protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){
+ if (pduDataStream == null) {
+ return null;
+ }
+ boolean keepParsing = true;
+ PduHeaders headers = new PduHeaders();
+
+ while (keepParsing && (pduDataStream.available() > 0)) {
+ pduDataStream.mark(1);
+ int headerField = extractByteValue(pduDataStream);
+ /* parse custom text header */
+ if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) {
+ pduDataStream.reset();
+ byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "TextHeader: " + new String(bVal));
+ }
+ /* we should ignore it at the moment */
+ continue;
+ }
+ switch (headerField) {
+ case PduHeaders.MESSAGE_TYPE:
+ {
+ int messageType = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: messageType: " + messageType);
+ }
+ switch (messageType) {
+ // We don't support these kind of messages now.
+ case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
+ case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
+ return null;
+ }
+ try {
+ headers.setOctet(messageType, headerField);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + messageType +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+ /* Octect value */
+ case PduHeaders.REPORT_ALLOWED:
+ case PduHeaders.ADAPTATION_ALLOWED:
+ case PduHeaders.DELIVERY_REPORT:
+ case PduHeaders.DRM_CONTENT:
+ case PduHeaders.DISTRIBUTION_INDICATOR:
+ case PduHeaders.QUOTAS:
+ case PduHeaders.READ_REPORT:
+ case PduHeaders.STORE:
+ case PduHeaders.STORED:
+ case PduHeaders.TOTALS:
+ case PduHeaders.SENDER_VISIBILITY:
+ case PduHeaders.READ_STATUS:
+ case PduHeaders.CANCEL_STATUS:
+ case PduHeaders.PRIORITY:
+ case PduHeaders.STATUS:
+ case PduHeaders.REPLY_CHARGING:
+ case PduHeaders.MM_STATE:
+ case PduHeaders.RECOMMENDED_RETRIEVAL_MODE:
+ case PduHeaders.CONTENT_CLASS:
+ case PduHeaders.RETRIEVE_STATUS:
+ case PduHeaders.STORE_STATUS:
+ /**
+ * The following field has a different value when
+ * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
+ * For now we ignore this fact, since we do not support these PDUs
+ */
+ case PduHeaders.RESPONSE_STATUS:
+ {
+ int value = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: byte: " + headerField + " value: " +
+ value);
+ }
+
+ try {
+ headers.setOctet(value, headerField);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + value +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Long-Integer */
+ case PduHeaders.DATE:
+ case PduHeaders.REPLY_CHARGING_SIZE:
+ case PduHeaders.MESSAGE_SIZE:
+ {
+ try {
+ long value = parseLongInteger(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: longint: " + headerField + " value: " +
+ value);
+ }
+ headers.setLongInteger(value, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Integer-Value */
+ case PduHeaders.MESSAGE_COUNT:
+ case PduHeaders.START:
+ case PduHeaders.LIMIT:
+ {
+ try {
+ long value = parseIntegerValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: int: " + headerField + " value: " +
+ value);
+ }
+ headers.setLongInteger(value, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Text-String */
+ case PduHeaders.TRANSACTION_ID:
+ case PduHeaders.REPLY_CHARGING_ID:
+ case PduHeaders.AUX_APPLIC_ID:
+ case PduHeaders.APPLIC_ID:
+ case PduHeaders.REPLY_APPLIC_ID:
+ /**
+ * The next three header fields are email addresses
+ * as defined in RFC2822,
+ * not including the characters "<" and ">"
+ */
+ case PduHeaders.MESSAGE_ID:
+ case PduHeaders.REPLACE_ID:
+ case PduHeaders.CANCEL_ID:
+ /**
+ * The following field has a different value when
+ * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
+ * For now we ignore this fact, since we do not support these PDUs
+ */
+ case PduHeaders.CONTENT_LOCATION:
+ {
+ byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != value) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: string: " + headerField + " value: " +
+ new String(value));
+ }
+ headers.setTextString(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Encoded-string-value */
+ case PduHeaders.SUBJECT:
+ case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT:
+ case PduHeaders.RETRIEVE_TEXT:
+ case PduHeaders.STATUS_TEXT:
+ case PduHeaders.STORE_STATUS_TEXT:
+ /* the next one is not support
+ * M-Mbox-Delete.conf and M-Delete.conf now */
+ case PduHeaders.RESPONSE_TEXT:
+ {
+ EncodedStringValue value =
+ parseEncodedStringValue(pduDataStream);
+ if (null != value) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: encoded string: " + headerField
+ + " value: " + value.getString());
+ }
+ headers.setEncodedStringValue(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch (RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Addressing model */
+ case PduHeaders.BCC:
+ case PduHeaders.CC:
+ case PduHeaders.TO:
+ {
+ EncodedStringValue value =
+ parseEncodedStringValue(pduDataStream);
+ if (null != value) {
+ byte[] address = value.getTextString();
+ if (null != address) {
+ String str = new String(address);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: (to/cc/bcc) address: " + headerField
+ + " value: " + str);
+ }
+ int endIndex = str.indexOf("/");
+ if (endIndex > 0) {
+ str = str.substring(0, endIndex);
+ }
+ try {
+ value.setTextString(str.getBytes());
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ return null;
+ }
+ }
+
+ try {
+ headers.appendEncodedStringValue(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value) */
+ case PduHeaders.DELIVERY_TIME:
+ case PduHeaders.EXPIRY:
+ case PduHeaders.REPLY_CHARGING_DEADLINE:
+ {
+ /* parse Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Absolute-token or Relative-token */
+ int token = extractByteValue(pduDataStream);
+
+ /* Date-value or Delta-seconds-value */
+ long timeValue;
+ try {
+ timeValue = parseLongInteger(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ if (PduHeaders.VALUE_RELATIVE_TOKEN == token) {
+ /* need to convert the Delta-seconds-value
+ * into Date-value */
+ timeValue = System.currentTimeMillis()/1000 + timeValue;
+ }
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: time value: " + headerField
+ + " value: " + timeValue);
+ }
+ headers.setLongInteger(timeValue, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.FROM: {
+ /* From-value =
+ * Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ */
+ EncodedStringValue from = null;
+ parseValueLength(pduDataStream); /* parse value-length */
+
+ /* Address-present-token or Insert-address-token */
+ int fromToken = extractByteValue(pduDataStream);
+
+ /* Address-present-token or Insert-address-token */
+ if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) {
+ /* Encoded-string-value */
+ from = parseEncodedStringValue(pduDataStream);
+ if (null != from) {
+ byte[] address = from.getTextString();
+ if (null != address) {
+ String str = new String(address);
+ int endIndex = str.indexOf("/");
+ if (endIndex > 0) {
+ str = str.substring(0, endIndex);
+ }
+ try {
+ from.setTextString(str.getBytes());
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ return null;
+ }
+ }
+ }
+ } else {
+ try {
+ from = new EncodedStringValue(
+ PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes());
+ } catch(NullPointerException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: from address: " + headerField
+ + " value: " + from.getString());
+ }
+ headers.setEncodedStringValue(from, PduHeaders.FROM);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.MESSAGE_CLASS: {
+ /* Message-class-value = Class-identifier | Token-text */
+ pduDataStream.mark(1);
+ int messageClass = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MESSAGE_CLASS: " + headerField
+ + " value: " + messageClass);
+ }
+
+ if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) {
+ /* Class-identifier */
+ try {
+ if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ }
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ } else {
+ /* Token-text */
+ pduDataStream.reset();
+ byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != messageClassString) {
+ try {
+ headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+ }
+ break;
+ }
+
+ case PduHeaders.MMS_VERSION: {
+ int version = parseShortInteger(pduDataStream);
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MMS_VERSION: " + headerField
+ + " value: " + version);
+ }
+ headers.setOctet(version, PduHeaders.MMS_VERSION);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + version +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.PREVIOUSLY_SENT_BY: {
+ /* Previously-sent-by-value =
+ * Value-length Forwarded-count-value Encoded-string-value */
+ /* parse value-length */
+ parseValueLength(pduDataStream);
+
+ /* parse Forwarded-count-value */
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* parse Encoded-string-value */
+ EncodedStringValue previouslySentBy =
+ parseEncodedStringValue(pduDataStream);
+ if (null != previouslySentBy) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_BY: " + headerField
+ + " value: " + previouslySentBy.getString());
+ }
+ headers.setEncodedStringValue(previouslySentBy,
+ PduHeaders.PREVIOUSLY_SENT_BY);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ case PduHeaders.PREVIOUSLY_SENT_DATE: {
+ /* Previously-sent-date-value =
+ * Value-length Forwarded-count-value Date-value */
+ /* parse value-length */
+ parseValueLength(pduDataStream);
+
+ /* parse Forwarded-count-value */
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* Date-value */
+ try {
+ long perviouslySentDate = parseLongInteger(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_DATE: " + headerField
+ + " value: " + perviouslySentDate);
+ }
+ headers.setLongInteger(perviouslySentDate,
+ PduHeaders.PREVIOUSLY_SENT_DATE);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.MM_FLAGS: {
+ /* MM-flags-value =
+ * Value-length
+ * ( Add-token | Remove-token | Filter-token )
+ * Encoded-string-value
+ */
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MM_FLAGS: " + headerField
+ + " NOT REALLY SUPPORTED");
+ }
+
+ /* parse Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Add-token | Remove-token | Filter-token */
+ extractByteValue(pduDataStream);
+
+ /* Encoded-string-value */
+ parseEncodedStringValue(pduDataStream);
+
+ /* not store this header filed in "headers",
+ * because now PduHeaders doesn't support it */
+ break;
+ }
+
+ /* Value-length
+ * (Message-total-token | Size-total-token) Integer-Value */
+ case PduHeaders.MBOX_TOTALS:
+ case PduHeaders.MBOX_QUOTAS:
+ {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MBOX_TOTALS: " + headerField);
+ }
+ /* Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Message-total-token | Size-total-token */
+ extractByteValue(pduDataStream);
+
+ /*Integer-Value*/
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* not store these headers filed in "headers",
+ because now PduHeaders doesn't support them */
+ break;
+ }
+
+ case PduHeaders.ELEMENT_DESCRIPTOR: {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: ELEMENT_DESCRIPTOR: " + headerField);
+ }
+ parseContentType(pduDataStream, null);
+
+ /* not store this header filed in "headers",
+ because now PduHeaders doesn't support it */
+ break;
+ }
+
+ case PduHeaders.CONTENT_TYPE: {
+ HashMap<Integer, Object> map =
+ new HashMap<Integer, Object>();
+ byte[] contentType =
+ parseContentType(pduDataStream, map);
+
+ if (null != contentType) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: CONTENT_TYPE: " + headerField +
+ contentType.toString());
+ }
+ headers.setTextString(contentType, PduHeaders.CONTENT_TYPE);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+
+ /* get start parameter */
+ mStartParam = (byte[]) map.get(PduPart.P_START);
+
+ /* get charset parameter */
+ mTypeParam= (byte[]) map.get(PduPart.P_TYPE);
+
+ keepParsing = false;
+ break;
+ }
+
+ case PduHeaders.CONTENT:
+ case PduHeaders.ADDITIONAL_HEADERS:
+ case PduHeaders.ATTRIBUTES:
+ default: {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: Unknown header: " + headerField);
+ }
+ log("Unknown header");
+ }
+ }
+ }
+
+ return headers;
+ }
+
+ /**
+ * Parse pdu parts.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return parts in PduBody structure
+ */
+ protected PduBody parseParts(ByteArrayInputStream pduDataStream) {
+ if (pduDataStream == null) {
+ return null;
+ }
+
+ int count = parseUnsignedInt(pduDataStream); // get the number of parts
+ PduBody body = new PduBody();
+
+ for (int i = 0 ; i < count ; i++) {
+ int headerLength = parseUnsignedInt(pduDataStream);
+ int dataLength = parseUnsignedInt(pduDataStream);
+ PduPart part = new PduPart();
+ int startPos = pduDataStream.available();
+ if (startPos <= 0) {
+ // Invalid part.
+ return null;
+ }
+
+ /* parse part's content-type */
+ HashMap<Integer, Object> map = new HashMap<Integer, Object>();
+ byte[] contentType = parseContentType(pduDataStream, map);
+ if (null != contentType) {
+ part.setContentType(contentType);
+ } else {
+ part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*"
+ }
+
+ /* get name parameter */
+ byte[] name = (byte[]) map.get(PduPart.P_NAME);
+ if (null != name) {
+ part.setName(name);
+ }
+
+ /* get charset parameter */
+ Integer charset = (Integer) map.get(PduPart.P_CHARSET);
+ if (null != charset) {
+ part.setCharset(charset);
+ }
+
+ /* parse part's headers */
+ int endPos = pduDataStream.available();
+ int partHeaderLen = headerLength - (startPos - endPos);
+ if (partHeaderLen > 0) {
+ if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) {
+ // Parse part header faild.
+ return null;
+ }
+ } else if (partHeaderLen < 0) {
+ // Invalid length of content-type.
+ return null;
+ }
+
+ /* FIXME: check content-id, name, filename and content location,
+ * if not set anyone of them, generate a default content-location
+ */
+ if ((null == part.getContentLocation())
+ && (null == part.getName())
+ && (null == part.getFilename())
+ && (null == part.getContentId())) {
+ part.setContentLocation(Long.toOctalString(
+ System.currentTimeMillis()).getBytes());
+ }
+
+ /* get part's data */
+ if (dataLength > 0) {
+ byte[] partData = new byte[dataLength];
+ String partContentType = new String(part.getContentType());
+ pduDataStream.read(partData, 0, dataLength);
+ if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) {
+ // parse "multipart/vnd.wap.multipart.alternative".
+ PduBody childBody = parseParts(new ByteArrayInputStream(partData));
+ // take the first part of children.
+ part = childBody.getPart(0);
+ } else {
+ // Check Content-Transfer-Encoding.
+ byte[] partDataEncoding = part.getContentTransferEncoding();
+ if (null != partDataEncoding) {
+ String encoding = new String(partDataEncoding);
+ if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) {
+ // Decode "base64" into "binary".
+ partData = Base64.decodeBase64(partData);
+ } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) {
+ // Decode "quoted-printable" into "binary".
+ partData = QuotedPrintable.decodeQuotedPrintable(partData);
+ } else {
+ // "binary" is the default encoding.
+ }
+ }
+ if (null == partData) {
+ log("Decode part data error!");
+ return null;
+ }
+ part.setData(partData);
+ }
+ }
+
+ /* add this part to body */
+ if (THE_FIRST_PART == checkPartPosition(part)) {
+ /* this is the first part */
+ body.addPart(0, part);
+ } else {
+ /* add the part to the end */
+ body.addPart(part);
+ }
+ }
+
+ return body;
+ }
+
+ /**
+ * Log status.
+ *
+ * @param text log information
+ */
+ @UnsupportedAppUsage
+ private static void log(String text) {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, text);
+ }
+ }
+
+ /**
+ * Parse unsigned integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the integer, -1 when failed
+ */
+ @UnsupportedAppUsage
+ protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * The maximum size of a uintvar is 32 bits.
+ * So it will be encoded in no more than 5 octets.
+ */
+ assert(null != pduDataStream);
+ int result = 0;
+ int temp = pduDataStream.read();
+ if (temp == -1) {
+ return temp;
+ }
+
+ while((temp & 0x80) != 0) {
+ result = result << 7;
+ result |= temp & 0x7F;
+ temp = pduDataStream.read();
+ if (temp == -1) {
+ return temp;
+ }
+ }
+
+ result = result << 7;
+ result |= temp & 0x7F;
+
+ return result;
+ }
+
+ /**
+ * Parse value length.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the integer
+ */
+ @UnsupportedAppUsage
+ protected static int parseValueLength(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Value-length = Short-length | (Length-quote Length)
+ * Short-length = <Any octet 0-30>
+ * Length-quote = <Octet 31>
+ * Length = Uintvar-integer
+ * Uintvar-integer = 1*5 OCTET
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int first = temp & 0xFF;
+
+ if (first <= SHORT_LENGTH_MAX) {
+ return first;
+ } else if (first == LENGTH_QUOTE) {
+ return parseUnsignedInt(pduDataStream);
+ }
+
+ throw new RuntimeException ("Value length > LENGTH_QUOTE!");
+ }
+
+ /**
+ * Parse encoded string value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the EncodedStringValue
+ */
+ protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){
+ /**
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+ assert(null != pduDataStream);
+ pduDataStream.mark(1);
+ EncodedStringValue returnValue = null;
+ int charset = 0;
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int first = temp & 0xFF;
+ if (first == 0) {
+ return new EncodedStringValue("");
+ }
+
+ pduDataStream.reset();
+ if (first < TEXT_MIN) {
+ parseValueLength(pduDataStream);
+
+ charset = parseShortInteger(pduDataStream); //get the "Charset"
+ }
+
+ byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+
+ try {
+ if (0 != charset) {
+ returnValue = new EncodedStringValue(charset, textString);
+ } else {
+ returnValue = new EncodedStringValue(textString);
+ }
+ } catch(Exception e) {
+ return null;
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Parse Text-String or Quoted-String.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING
+ * @return the string without End-of-string in byte array
+ */
+ @UnsupportedAppUsage
+ protected static byte[] parseWapString(ByteArrayInputStream pduDataStream,
+ int stringType) {
+ assert(null != pduDataStream);
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Text-string = [Quote] *TEXT End-of-string
+ * If the first character in the TEXT is in the range of 128-255,
+ * a Quote character must precede it.
+ * Otherwise the Quote character must be omitted.
+ * The Quote is not part of the contents.
+ * Quote = <Octet 127>
+ * End-of-string = <Octet 0>
+ *
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ *
+ * Token-text = Token End-of-string
+ */
+
+ // Mark supposed beginning of Text-string
+ // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG
+ pduDataStream.mark(1);
+
+ // Check first char
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ if ((TYPE_QUOTED_STRING == stringType) &&
+ (QUOTED_STRING_FLAG == temp)) {
+ // Mark again if QUOTED_STRING_FLAG and ignore it
+ pduDataStream.mark(1);
+ } else if ((TYPE_TEXT_STRING == stringType) &&
+ (QUOTE == temp)) {
+ // Mark again if QUOTE and ignore it
+ pduDataStream.mark(1);
+ } else {
+ // Otherwise go back to origin
+ pduDataStream.reset();
+ }
+
+ // We are now definitely at the beginning of string
+ /**
+ * Return *TOKEN or *TEXT (Text-String without QUOTE,
+ * Quoted-String without QUOTED_STRING_FLAG and without End-of-string)
+ */
+ return getWapString(pduDataStream, stringType);
+ }
+
+ /**
+ * Check TOKEN data defined in RFC2616.
+ * @param ch checking data
+ * @return true when ch is TOKEN, false when ch is not TOKEN
+ */
+ protected static boolean isTokenCharacter(int ch) {
+ /**
+ * Token = 1*<any CHAR except CTLs or separators>
+ * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64)
+ * | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34)
+ * | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61)
+ * | "{"(123) | "}"(125) | SP(32) | HT(9)
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * SP = <US-ASCII SP, space (32)>
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ */
+ if((ch < 33) || (ch > 126)) {
+ return false;
+ }
+
+ switch(ch) {
+ case '"': /* '"' */
+ case '(': /* '(' */
+ case ')': /* ')' */
+ case ',': /* ',' */
+ case '/': /* '/' */
+ case ':': /* ':' */
+ case ';': /* ';' */
+ case '<': /* '<' */
+ case '=': /* '=' */
+ case '>': /* '>' */
+ case '?': /* '?' */
+ case '@': /* '@' */
+ case '[': /* '[' */
+ case '\\': /* '\' */
+ case ']': /* ']' */
+ case '{': /* '{' */
+ case '}': /* '}' */
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check TEXT data defined in RFC2616.
+ * @param ch checking data
+ * @return true when ch is TEXT, false when ch is not TEXT
+ */
+ protected static boolean isText(int ch) {
+ /**
+ * TEXT = <any OCTET except CTLs,
+ * but including LWS>
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * LWS = [CRLF] 1*( SP | HT )
+ * CRLF = CR LF
+ * CR = <US-ASCII CR, carriage return (13)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ */
+ if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) {
+ return true;
+ }
+
+ switch(ch) {
+ case '\t': /* '\t' */
+ case '\n': /* '\n' */
+ case '\r': /* '\r' */
+ return true;
+ }
+
+ return false;
+ }
+
+ protected static byte[] getWapString(ByteArrayInputStream pduDataStream,
+ int stringType) {
+ assert(null != pduDataStream);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ while((-1 != temp) && ('\0' != temp)) {
+ // check each of the character
+ if (stringType == TYPE_TOKEN_STRING) {
+ if (isTokenCharacter(temp)) {
+ out.write(temp);
+ }
+ } else {
+ if (isText(temp)) {
+ out.write(temp);
+ }
+ }
+
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ }
+
+ if (out.size() > 0) {
+ return out.toByteArray();
+ }
+
+ return null;
+ }
+
+ /**
+ * Extract a byte value from the input stream.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the byte
+ */
+ protected static int extractByteValue(ByteArrayInputStream pduDataStream) {
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ return temp & 0xFF;
+ }
+
+ /**
+ * Parse Short-Integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the byte
+ */
+ @UnsupportedAppUsage
+ protected static int parseShortInteger(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Short-integer = OCTET
+ * Integers in range 0-127 shall be encoded as a one
+ * octet value with the most significant bit set to one (1xxx xxxx)
+ * and with the value in the remaining least significant bits.
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ return temp & 0x7F;
+ }
+
+ /**
+ * Parse Long-Integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return long integer
+ */
+ protected static long parseLongInteger(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Long-integer = Short-length Multi-octet-integer
+ * The Short-length indicates the length of the Multi-octet-integer
+ * Multi-octet-integer = 1*30 OCTET
+ * The content octets shall be an unsigned integer value
+ * with the most significant octet encoded first (big-endian representation).
+ * The minimum number of octets must be used to encode the value.
+ * Short-length = <Any octet 0-30>
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int count = temp & 0xFF;
+
+ if (count > LONG_INTEGER_LENGTH_MAX) {
+ throw new RuntimeException("Octet count greater than 8 and I can't represent that!");
+ }
+
+ long result = 0;
+
+ for (int i = 0 ; i < count ; i++) {
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ result <<= 8;
+ result += (temp & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parse Integer-Value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return long integer
+ */
+ protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Integer-Value = Short-integer | Long-integer
+ */
+ assert(null != pduDataStream);
+ pduDataStream.mark(1);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+ if (temp > SHORT_INTEGER_MAX) {
+ return parseShortInteger(pduDataStream);
+ } else {
+ return parseLongInteger(pduDataStream);
+ }
+ }
+
+ /**
+ * To skip length of the wap value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param length area size
+ * @return the values in this area
+ */
+ protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) {
+ assert(null != pduDataStream);
+ byte[] area = new byte[length];
+ int readLen = pduDataStream.read(area, 0, length);
+ if (readLen < length) { //The actually read length is lower than the length
+ return -1;
+ } else {
+ return readLen;
+ }
+ }
+
+ /**
+ * Parse content type parameters. For now we just support
+ * four parameters used in mms: "type", "start", "name", "charset".
+ *
+ * @param pduDataStream pdu data input stream
+ * @param map to store parameters of Content-Type field
+ * @param length length of all the parameters
+ */
+ protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream,
+ HashMap<Integer, Object> map, Integer length) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Parameter = Typed-parameter | Untyped-parameter
+ * Typed-parameter = Well-known-parameter-token Typed-value
+ * the actual expected type of the value is implied by the well-known parameter
+ * Well-known-parameter-token = Integer-value
+ * the code values used for parameters are specified in the Assigned Numbers appendix
+ * Typed-value = Compact-value | Text-value
+ * In addition to the expected type, there may be no value.
+ * If the value cannot be encoded using the expected type, it shall be encoded as text.
+ * Compact-value = Integer-value |
+ * Date-value | Delta-seconds-value | Q-value | Version-value |
+ * Uri-value
+ * Untyped-parameter = Token-text Untyped-value
+ * the type of the value is unknown, but it shall be encoded as an integer,
+ * if that is possible.
+ * Untyped-value = Integer-value | Text-value
+ */
+ assert(null != pduDataStream);
+ assert(length > 0);
+
+ int startPos = pduDataStream.available();
+ int tempPos = 0;
+ int lastLen = length;
+ while(0 < lastLen) {
+ int param = pduDataStream.read();
+ assert(-1 != param);
+ lastLen--;
+
+ switch (param) {
+ /**
+ * From rfc2387, chapter 3.1
+ * The type parameter must be specified and its value is the MIME media
+ * type of the "root" body part. It permits a MIME user agent to
+ * determine the content-type without reference to the enclosed body
+ * part. If the value of the type parameter and the root body part's
+ * content-type differ then the User Agent's behavior is undefined.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * type = Constrained-encoding
+ * Constrained-encoding = Extension-Media | Short-integer
+ * Extension-media = *TEXT End-of-string
+ */
+ case PduPart.P_TYPE:
+ case PduPart.P_CT_MR_TYPE:
+ pduDataStream.mark(1);
+ int first = extractByteValue(pduDataStream);
+ pduDataStream.reset();
+ if (first > TEXT_MAX) {
+ // Short-integer (well-known type)
+ int index = parseShortInteger(pduDataStream);
+
+ if (index < PduContentTypes.contentTypes.length) {
+ byte[] type = (PduContentTypes.contentTypes[index]).getBytes();
+ map.put(PduPart.P_TYPE, type);
+ } else {
+ //not support this type, ignore it.
+ }
+ } else {
+ // Text-String (extension-media)
+ byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != type) && (null != map)) {
+ map.put(PduPart.P_TYPE, type);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3.
+ * Start Parameter Referring to Presentation
+ *
+ * From rfc2387, chapter 3.2
+ * The start parameter, if given, is the content-ID of the compound
+ * object's "root". If not present the "root" is the first body part in
+ * the Multipart/Related entity. The "root" is the element the
+ * applications processes first.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * start = Text-String
+ */
+ case PduPart.P_START:
+ case PduPart.P_DEP_START:
+ byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != start) && (null != map)) {
+ map.put(PduPart.P_START, start);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf
+ * In creation, the character set SHALL be either us-ascii
+ * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode].
+ * In retrieval, both us-ascii and utf-8 SHALL be supported.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * charset = Well-known-charset|Text-String
+ * Well-known-charset = Any-charset | Integer-value
+ * Both are encoded using values from Character Set
+ * Assignments table in Assigned Numbers
+ * Any-charset = <Octet 128>
+ * Equivalent to the special RFC2616 charset value "*"
+ */
+ case PduPart.P_CHARSET:
+ pduDataStream.mark(1);
+ int firstValue = extractByteValue(pduDataStream);
+ pduDataStream.reset();
+ //Check first char
+ if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) ||
+ (END_STRING_FLAG == firstValue)) {
+ //Text-String (extension-charset)
+ byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ try {
+ int charsetInt = CharacterSets.getMibEnumValue(
+ new String(charsetStr));
+ map.put(PduPart.P_CHARSET, charsetInt);
+ } catch (UnsupportedEncodingException e) {
+ // Not a well-known charset, use "*".
+ Log.e(LOG_TAG, Arrays.toString(charsetStr), e);
+ map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET);
+ }
+ } else {
+ //Well-known-charset
+ int charset = (int) parseIntegerValue(pduDataStream);
+ if (map != null) {
+ map.put(PduPart.P_CHARSET, charset);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf
+ * A name for multipart object SHALL be encoded using name-parameter
+ * for Content-Type header in WSP multipart headers.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * name = Text-String
+ */
+ case PduPart.P_DEP_NAME:
+ case PduPart.P_NAME:
+ byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != name) && (null != map)) {
+ map.put(PduPart.P_NAME, name);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ default:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Content-Type parameter");
+ }
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Content-Type");
+ } else {
+ lastLen = 0;
+ }
+ break;
+ }
+ }
+
+ if (0 != lastLen) {
+ Log.e(LOG_TAG, "Corrupt Content-Type");
+ }
+ }
+
+ /**
+ * Parse content type.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param map to store parameters in Content-Type header field
+ * @return Content-Type value
+ */
+ @UnsupportedAppUsage
+ protected static byte[] parseContentType(ByteArrayInputStream pduDataStream,
+ HashMap<Integer, Object> map) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Content-type-value = Constrained-media | Content-general-form
+ * Content-general-form = Value-length Media-type
+ * Media-type = (Well-known-media | Extension-Media) *(Parameter)
+ */
+ assert(null != pduDataStream);
+
+ byte[] contentType = null;
+ pduDataStream.mark(1);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+
+ int cur = (temp & 0xFF);
+
+ if (cur < TEXT_MIN) {
+ int length = parseValueLength(pduDataStream);
+ int startPos = pduDataStream.available();
+ pduDataStream.mark(1);
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+ int first = (temp & 0xFF);
+
+ if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) {
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ } else if (first > TEXT_MAX) {
+ int index = parseShortInteger(pduDataStream);
+
+ if (index < PduContentTypes.contentTypes.length) { //well-known type
+ contentType = (PduContentTypes.contentTypes[index]).getBytes();
+ } else {
+ pduDataStream.reset();
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ }
+ } else {
+ Log.e(LOG_TAG, "Corrupt content-type");
+ return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
+ }
+
+ int endPos = pduDataStream.available();
+ int parameterLen = length - (startPos - endPos);
+ if (parameterLen > 0) {//have parameters
+ parseContentTypeParams(pduDataStream, map, parameterLen);
+ }
+
+ if (parameterLen < 0) {
+ Log.e(LOG_TAG, "Corrupt MMS message");
+ return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
+ }
+ } else if (cur <= TEXT_MAX) {
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ } else {
+ contentType =
+ (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes();
+ }
+
+ return contentType;
+ }
+
+ /**
+ * Parse part's headers.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param part to store the header informations of the part
+ * @param length length of the headers
+ * @return true if parse successfully, false otherwise
+ */
+ @UnsupportedAppUsage
+ protected boolean parsePartHeaders(ByteArrayInputStream pduDataStream,
+ PduPart part, int length) {
+ assert(null != pduDataStream);
+ assert(null != part);
+ assert(length > 0);
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.
+ * A name for multipart object SHALL be encoded using name-parameter
+ * for Content-Type header in WSP multipart headers.
+ * In decoding, name-parameter of Content-Type SHALL be used if available.
+ * If name-parameter of Content-Type is not available,
+ * filename parameter of Content-Disposition header SHALL be used if available.
+ * If neither name-parameter of Content-Type header nor filename parameter
+ * of Content-Disposition header is available,
+ * Content-Location header SHALL be used if available.
+ *
+ * Within SMIL part the reference to the media object parts SHALL use
+ * either Content-ID or Content-Location mechanism [RFC2557]
+ * and the corresponding WSP part headers in media object parts
+ * contain the corresponding definitions.
+ */
+ int startPos = pduDataStream.available();
+ int tempPos = 0;
+ int lastLen = length;
+ while(0 < lastLen) {
+ int header = pduDataStream.read();
+ assert(-1 != header);
+ lastLen--;
+
+ if (header > TEXT_MAX) {
+ // Number assigned headers.
+ switch (header) {
+ case PduPart.P_CONTENT_LOCATION:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-location-value = Uri-value
+ */
+ byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != contentLocation) {
+ part.setContentLocation(contentLocation);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ case PduPart.P_CONTENT_ID:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-ID-value = Quoted-string
+ */
+ byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING);
+ if (null != contentId) {
+ part.setContentId(contentId);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ case PduPart.P_DEP_CONTENT_DISPOSITION:
+ case PduPart.P_CONTENT_DISPOSITION:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-disposition-value = Value-length Disposition *(Parameter)
+ * Disposition = Form-data | Attachment | Inline | Token-text
+ * Form-data = <Octet 128>
+ * Attachment = <Octet 129>
+ * Inline = <Octet 130>
+ */
+
+ /*
+ * some carrier mmsc servers do not support content_disposition
+ * field correctly
+ */
+ if (mParseContentDisposition) {
+ int len = parseValueLength(pduDataStream);
+ pduDataStream.mark(1);
+ int thisStartPos = pduDataStream.available();
+ int thisEndPos = 0;
+ int value = pduDataStream.read();
+
+ if (value == PduPart.P_DISPOSITION_FROM_DATA ) {
+ part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA);
+ } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) {
+ part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT);
+ } else if (value == PduPart.P_DISPOSITION_INLINE) {
+ part.setContentDisposition(PduPart.DISPOSITION_INLINE);
+ } else {
+ pduDataStream.reset();
+ /* Token-text */
+ part.setContentDisposition(parseWapString(pduDataStream
+ , TYPE_TEXT_STRING));
+ }
+
+ /* get filename parameter and skip other parameters */
+ thisEndPos = pduDataStream.available();
+ if (thisStartPos - thisEndPos < len) {
+ value = pduDataStream.read();
+ if (value == PduPart.P_FILENAME) { //filename is text-string
+ part.setFilename(parseWapString(pduDataStream
+ , TYPE_TEXT_STRING));
+ }
+
+ /* skip other parameters */
+ thisEndPos = pduDataStream.available();
+ if (thisStartPos - thisEndPos < len) {
+ int last = len - (thisStartPos - thisEndPos);
+ byte[] temp = new byte[last];
+ pduDataStream.read(temp, 0, last);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ }
+ break;
+ default:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Part headers: " + header);
+ }
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+ lastLen = 0;
+ break;
+ }
+ } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) {
+ // Not assigned header.
+ byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+
+ // Check the header whether it is "Content-Transfer-Encoding".
+ if (true ==
+ PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) {
+ part.setContentTransferEncoding(tempValue);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ } else {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Part headers: " + header);
+ }
+ // Skip all headers of this part.
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+ lastLen = 0;
+ }
+ }
+
+ if (0 != lastLen) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check the position of a specified part.
+ *
+ * @param part the part to be checked
+ * @return part position, THE_FIRST_PART when it's the
+ * first one, THE_LAST_PART when it's the last one.
+ */
+ @UnsupportedAppUsage
+ private static int checkPartPosition(PduPart part) {
+ assert(null != part);
+ if ((null == mTypeParam) &&
+ (null == mStartParam)) {
+ return THE_LAST_PART;
+ }
+
+ /* check part's content-id */
+ if (null != mStartParam) {
+ byte[] contentId = part.getContentId();
+ if (null != contentId) {
+ if (true == Arrays.equals(mStartParam, contentId)) {
+ return THE_FIRST_PART;
+ }
+ }
+ // This is not the first part, so append to end (keeping the original order)
+ // Check b/19607294 for details of this change
+ return THE_LAST_PART;
+ }
+
+ /* check part's content-type */
+ if (null != mTypeParam) {
+ byte[] contentType = part.getContentType();
+ if (null != contentType) {
+ if (true == Arrays.equals(mTypeParam, contentType)) {
+ return THE_FIRST_PART;
+ }
+ }
+ }
+
+ return THE_LAST_PART;
+ }
+
+ /**
+ * Check mandatory headers of a pdu.
+ *
+ * @param headers pdu headers
+ * @return true if the pdu has all of the mandatory headers, false otherwise.
+ */
+ protected static boolean checkMandatoryHeader(PduHeaders headers) {
+ if (null == headers) {
+ return false;
+ }
+
+ /* get message type */
+ int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
+
+ /* check Mms-Version field */
+ int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION);
+ if (0 == mmsVersion) {
+ // Every message should have Mms-Version field.
+ return false;
+ }
+
+ /* check mandatory header fields */
+ switch (messageType) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ // Content-Type field.
+ byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
+ if (null == srContentType) {
+ return false;
+ }
+
+ // From field.
+ EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == srFrom) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == srTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ // Response-Status field.
+ int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS);
+ if (0 == scResponseStatus) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == scTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ // Content-Location field.
+ byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION);
+ if (null == niContentLocation) {
+ return false;
+ }
+
+ // Expiry field.
+ long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY);
+ if (-1 == niExpiry) {
+ return false;
+ }
+
+ // Message-Class field.
+ byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS);
+ if (null == niMessageClass) {
+ return false;
+ }
+
+ // Message-Size field.
+ long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ if (-1 == niMessageSize) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == niTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ // Status field.
+ int nriStatus = headers.getOctet(PduHeaders.STATUS);
+ if (0 == nriStatus) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == nriTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ // Content-Type field.
+ byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
+ if (null == rcContentType) {
+ return false;
+ }
+
+ // Date field.
+ long rcDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == rcDate) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ // Date field.
+ long diDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == diDate) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == diMessageId) {
+ return false;
+ }
+
+ // Status field.
+ int diStatus = headers.getOctet(PduHeaders.STATUS);
+ if (0 == diStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == diTo) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ // Transaction-Id field.
+ byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == aiTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ // Date field.
+ long roDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == roDate) {
+ return false;
+ }
+
+ // From field.
+ EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == roFrom) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == roMessageId) {
+ return false;
+ }
+
+ // Read-Status field.
+ int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
+ if (0 == roReadStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == roTo) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ // From field.
+ EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == rrFrom) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == rrMessageId) {
+ return false;
+ }
+
+ // Read-Status field.
+ int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
+ if (0 == rrReadStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == rrTo) {
+ return false;
+ }
+
+ break;
+ default:
+ // Parser doesn't support this message type in this version.
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/PduPart.java b/telephony/common/com/google/android/mms/pdu/PduPart.java
new file mode 100644
index 0000000..09b7751
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/PduPart.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 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.google.android.mms.pdu;
+
+import android.net.Uri;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The pdu part.
+ */
+public class PduPart {
+ /**
+ * Well-Known Parameters.
+ */
+ public static final int P_Q = 0x80;
+ public static final int P_CHARSET = 0x81;
+ public static final int P_LEVEL = 0x82;
+ public static final int P_TYPE = 0x83;
+ public static final int P_DEP_NAME = 0x85;
+ public static final int P_DEP_FILENAME = 0x86;
+ public static final int P_DIFFERENCES = 0x87;
+ public static final int P_PADDING = 0x88;
+ // This value of "TYPE" s used with Content-Type: multipart/related
+ public static final int P_CT_MR_TYPE = 0x89;
+ public static final int P_DEP_START = 0x8A;
+ public static final int P_DEP_START_INFO = 0x8B;
+ public static final int P_DEP_COMMENT = 0x8C;
+ public static final int P_DEP_DOMAIN = 0x8D;
+ public static final int P_MAX_AGE = 0x8E;
+ public static final int P_DEP_PATH = 0x8F;
+ public static final int P_SECURE = 0x90;
+ public static final int P_SEC = 0x91;
+ public static final int P_MAC = 0x92;
+ public static final int P_CREATION_DATE = 0x93;
+ public static final int P_MODIFICATION_DATE = 0x94;
+ public static final int P_READ_DATE = 0x95;
+ public static final int P_SIZE = 0x96;
+ public static final int P_NAME = 0x97;
+ public static final int P_FILENAME = 0x98;
+ public static final int P_START = 0x99;
+ public static final int P_START_INFO = 0x9A;
+ public static final int P_COMMENT = 0x9B;
+ public static final int P_DOMAIN = 0x9C;
+ public static final int P_PATH = 0x9D;
+
+ /**
+ * Header field names.
+ */
+ public static final int P_CONTENT_TYPE = 0x91;
+ public static final int P_CONTENT_LOCATION = 0x8E;
+ public static final int P_CONTENT_ID = 0xC0;
+ public static final int P_DEP_CONTENT_DISPOSITION = 0xAE;
+ public static final int P_CONTENT_DISPOSITION = 0xC5;
+ // The next header is unassigned header, use reserved header(0x48) value.
+ public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8;
+
+ /**
+ * Content=Transfer-Encoding string.
+ */
+ public static final String CONTENT_TRANSFER_ENCODING =
+ "Content-Transfer-Encoding";
+
+ /**
+ * Value of Content-Transfer-Encoding.
+ */
+ public static final String P_BINARY = "binary";
+ public static final String P_7BIT = "7bit";
+ public static final String P_8BIT = "8bit";
+ public static final String P_BASE64 = "base64";
+ public static final String P_QUOTED_PRINTABLE = "quoted-printable";
+
+ /**
+ * Value of disposition can be set to PduPart when the value is octet in
+ * the PDU.
+ * "from-data" instead of Form-data<Octet 128>.
+ * "attachment" instead of Attachment<Octet 129>.
+ * "inline" instead of Inline<Octet 130>.
+ */
+ static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes();
+ static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes();
+ static final byte[] DISPOSITION_INLINE = "inline".getBytes();
+
+ /**
+ * Content-Disposition value.
+ */
+ public static final int P_DISPOSITION_FROM_DATA = 0x80;
+ public static final int P_DISPOSITION_ATTACHMENT = 0x81;
+ public static final int P_DISPOSITION_INLINE = 0x82;
+
+ /**
+ * Header of part.
+ */
+ private Map<Integer, Object> mPartHeader = null;
+
+ /**
+ * Data uri.
+ */
+ private Uri mUri = null;
+
+ /**
+ * Part data.
+ */
+ private byte[] mPartData = null;
+
+ private static final String TAG = "PduPart";
+
+ /**
+ * Empty Constructor.
+ */
+ @UnsupportedAppUsage
+ public PduPart() {
+ mPartHeader = new HashMap<Integer, Object>();
+ }
+
+ /**
+ * Set part data. The data are stored as byte array.
+ *
+ * @param data the data
+ */
+ @UnsupportedAppUsage
+ public void setData(byte[] data) {
+ if(data == null) {
+ return;
+ }
+
+ mPartData = new byte[data.length];
+ System.arraycopy(data, 0, mPartData, 0, data.length);
+ }
+
+ /**
+ * @return A copy of the part data or null if the data wasn't set or
+ * the data is stored as Uri.
+ * @see #getDataUri
+ */
+ @UnsupportedAppUsage
+ public byte[] getData() {
+ if(mPartData == null) {
+ return null;
+ }
+
+ byte[] byteArray = new byte[mPartData.length];
+ System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length);
+ return byteArray;
+ }
+
+ /**
+ * @return The length of the data, if this object have data, else 0.
+ */
+ @UnsupportedAppUsage
+ public int getDataLength() {
+ if(mPartData != null){
+ return mPartData.length;
+ } else {
+ return 0;
+ }
+ }
+
+
+ /**
+ * Set data uri. The data are stored as Uri.
+ *
+ * @param uri the uri
+ */
+ @UnsupportedAppUsage
+ public void setDataUri(Uri uri) {
+ mUri = uri;
+ }
+
+ /**
+ * @return The Uri of the part data or null if the data wasn't set or
+ * the data is stored as byte array.
+ * @see #getData
+ */
+ @UnsupportedAppUsage
+ public Uri getDataUri() {
+ return mUri;
+ }
+
+ /**
+ * Set Content-id value
+ *
+ * @param contentId the content-id value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentId(byte[] contentId) {
+ if((contentId == null) || (contentId.length == 0)) {
+ throw new IllegalArgumentException(
+ "Content-Id may not be null or empty.");
+ }
+
+ if ((contentId.length > 1)
+ && ((char) contentId[0] == '<')
+ && ((char) contentId[contentId.length - 1] == '>')) {
+ mPartHeader.put(P_CONTENT_ID, contentId);
+ return;
+ }
+
+ // Insert beginning '<' and trailing '>' for Content-Id.
+ byte[] buffer = new byte[contentId.length + 2];
+ buffer[0] = (byte) (0xff & '<');
+ buffer[buffer.length - 1] = (byte) (0xff & '>');
+ System.arraycopy(contentId, 0, buffer, 1, contentId.length);
+ mPartHeader.put(P_CONTENT_ID, buffer);
+ }
+
+ /**
+ * Get Content-id value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentId() {
+ return (byte[]) mPartHeader.get(P_CONTENT_ID);
+ }
+
+ /**
+ * Set Char-set value.
+ *
+ * @param charset the value
+ */
+ @UnsupportedAppUsage
+ public void setCharset(int charset) {
+ mPartHeader.put(P_CHARSET, charset);
+ }
+
+ /**
+ * Get Char-set value
+ *
+ * @return the charset value. Return 0 if charset was not set.
+ */
+ @UnsupportedAppUsage
+ public int getCharset() {
+ Integer charset = (Integer) mPartHeader.get(P_CHARSET);
+ if(charset == null) {
+ return 0;
+ } else {
+ return charset.intValue();
+ }
+ }
+
+ /**
+ * Set Content-Location value.
+ *
+ * @param contentLocation the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentLocation(byte[] contentLocation) {
+ if(contentLocation == null) {
+ throw new NullPointerException("null content-location");
+ }
+
+ mPartHeader.put(P_CONTENT_LOCATION, contentLocation);
+ }
+
+ /**
+ * Get Content-Location value.
+ *
+ * @return the value
+ * return PduPart.disposition[0] instead of <Octet 128> (Form-data).
+ * return PduPart.disposition[1] instead of <Octet 129> (Attachment).
+ * return PduPart.disposition[2] instead of <Octet 130> (Inline).
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentLocation() {
+ return (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
+ }
+
+ /**
+ * Set Content-Disposition value.
+ * Use PduPart.disposition[0] instead of <Octet 128> (Form-data).
+ * Use PduPart.disposition[1] instead of <Octet 129> (Attachment).
+ * Use PduPart.disposition[2] instead of <Octet 130> (Inline).
+ *
+ * @param contentDisposition the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentDisposition(byte[] contentDisposition) {
+ if(contentDisposition == null) {
+ throw new NullPointerException("null content-disposition");
+ }
+
+ mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition);
+ }
+
+ /**
+ * Get Content-Disposition value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentDisposition() {
+ return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION);
+ }
+
+ /**
+ * Set Content-Type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] contentType) {
+ if(contentType == null) {
+ throw new NullPointerException("null content-type");
+ }
+
+ mPartHeader.put(P_CONTENT_TYPE, contentType);
+ }
+
+ /**
+ * Get Content-Type value of part.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return (byte[]) mPartHeader.get(P_CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-Transfer-Encoding value
+ *
+ * @param contentId the content-id value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentTransferEncoding(byte[] contentTransferEncoding) {
+ if(contentTransferEncoding == null) {
+ throw new NullPointerException("null content-transfer-encoding");
+ }
+
+ mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
+ }
+
+ /**
+ * Get Content-Transfer-Encoding value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentTransferEncoding() {
+ return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING);
+ }
+
+ /**
+ * Set Content-type parameter: name.
+ *
+ * @param name the name value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setName(byte[] name) {
+ if(null == name) {
+ throw new NullPointerException("null content-id");
+ }
+
+ mPartHeader.put(P_NAME, name);
+ }
+
+ /**
+ * Get content-type parameter: name.
+ *
+ * @return the name
+ */
+ @UnsupportedAppUsage
+ public byte[] getName() {
+ return (byte[]) mPartHeader.get(P_NAME);
+ }
+
+ /**
+ * Get Content-disposition parameter: filename
+ *
+ * @param fileName the filename value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFilename(byte[] fileName) {
+ if(null == fileName) {
+ throw new NullPointerException("null content-id");
+ }
+
+ mPartHeader.put(P_FILENAME, fileName);
+ }
+
+ /**
+ * Set Content-disposition parameter: filename
+ *
+ * @return the filename
+ */
+ @UnsupportedAppUsage
+ public byte[] getFilename() {
+ return (byte[]) mPartHeader.get(P_FILENAME);
+ }
+
+ @UnsupportedAppUsage
+ public String generateLocation() {
+ // Assumption: At least one of the content-location / name / filename
+ // or content-id should be set. This is guaranteed by the PduParser
+ // for incoming messages and by MM composer for outgoing messages.
+ byte[] location = (byte[]) mPartHeader.get(P_NAME);
+ if(null == location) {
+ location = (byte[]) mPartHeader.get(P_FILENAME);
+
+ if (null == location) {
+ location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
+ }
+ }
+
+ if (null == location) {
+ byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID);
+ return "cid:" + new String(contentId);
+ } else {
+ return new String(location);
+ }
+ }
+}
+
diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java
new file mode 100755
index 0000000..93f3065
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java
@@ -0,0 +1,1573 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 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.google.android.mms.pdu;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.drm.DrmManagerClient;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.Telephony;
+import android.provider.Telephony.Mms;
+import android.provider.Telephony.Mms.Addr;
+import android.provider.Telephony.Mms.Part;
+import android.provider.Telephony.MmsSms;
+import android.provider.Telephony.MmsSms.PendingMessages;
+import android.provider.Telephony.Threads;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
+import com.google.android.mms.util.PduCache;
+import com.google.android.mms.util.PduCacheEntry;
+import com.google.android.mms.util.SqliteWrapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * This class is the high-level manager of PDU storage.
+ */
+public class PduPersister {
+ private static final String TAG = "PduPersister";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final long DUMMY_THREAD_ID = Long.MAX_VALUE;
+
+ /**
+ * The uri of temporary drm objects.
+ */
+ public static final String TEMPORARY_DRM_OBJECT_URI =
+ "content://mms/" + Long.MAX_VALUE + "/part";
+ /**
+ * Indicate that we transiently failed to process a MM.
+ */
+ public static final int PROC_STATUS_TRANSIENT_FAILURE = 1;
+ /**
+ * Indicate that we permanently failed to process a MM.
+ */
+ public static final int PROC_STATUS_PERMANENTLY_FAILURE = 2;
+ /**
+ * Indicate that we have successfully processed a MM.
+ */
+ public static final int PROC_STATUS_COMPLETED = 3;
+
+ private static PduPersister sPersister;
+ @UnsupportedAppUsage
+ private static final PduCache PDU_CACHE_INSTANCE;
+
+ @UnsupportedAppUsage
+ private static final int[] ADDRESS_FIELDS = new int[] {
+ PduHeaders.BCC,
+ PduHeaders.CC,
+ PduHeaders.FROM,
+ PduHeaders.TO
+ };
+
+ private static final String[] PDU_PROJECTION = new String[] {
+ Mms._ID,
+ Mms.MESSAGE_BOX,
+ Mms.THREAD_ID,
+ Mms.RETRIEVE_TEXT,
+ Mms.SUBJECT,
+ Mms.CONTENT_LOCATION,
+ Mms.CONTENT_TYPE,
+ Mms.MESSAGE_CLASS,
+ Mms.MESSAGE_ID,
+ Mms.RESPONSE_TEXT,
+ Mms.TRANSACTION_ID,
+ Mms.CONTENT_CLASS,
+ Mms.DELIVERY_REPORT,
+ Mms.MESSAGE_TYPE,
+ Mms.MMS_VERSION,
+ Mms.PRIORITY,
+ Mms.READ_REPORT,
+ Mms.READ_STATUS,
+ Mms.REPORT_ALLOWED,
+ Mms.RETRIEVE_STATUS,
+ Mms.STATUS,
+ Mms.DATE,
+ Mms.DELIVERY_TIME,
+ Mms.EXPIRY,
+ Mms.MESSAGE_SIZE,
+ Mms.SUBJECT_CHARSET,
+ Mms.RETRIEVE_TEXT_CHARSET,
+ };
+
+ private static final int PDU_COLUMN_ID = 0;
+ private static final int PDU_COLUMN_MESSAGE_BOX = 1;
+ private static final int PDU_COLUMN_THREAD_ID = 2;
+ private static final int PDU_COLUMN_RETRIEVE_TEXT = 3;
+ private static final int PDU_COLUMN_SUBJECT = 4;
+ private static final int PDU_COLUMN_CONTENT_LOCATION = 5;
+ private static final int PDU_COLUMN_CONTENT_TYPE = 6;
+ private static final int PDU_COLUMN_MESSAGE_CLASS = 7;
+ private static final int PDU_COLUMN_MESSAGE_ID = 8;
+ private static final int PDU_COLUMN_RESPONSE_TEXT = 9;
+ private static final int PDU_COLUMN_TRANSACTION_ID = 10;
+ private static final int PDU_COLUMN_CONTENT_CLASS = 11;
+ private static final int PDU_COLUMN_DELIVERY_REPORT = 12;
+ private static final int PDU_COLUMN_MESSAGE_TYPE = 13;
+ private static final int PDU_COLUMN_MMS_VERSION = 14;
+ private static final int PDU_COLUMN_PRIORITY = 15;
+ private static final int PDU_COLUMN_READ_REPORT = 16;
+ private static final int PDU_COLUMN_READ_STATUS = 17;
+ private static final int PDU_COLUMN_REPORT_ALLOWED = 18;
+ private static final int PDU_COLUMN_RETRIEVE_STATUS = 19;
+ private static final int PDU_COLUMN_STATUS = 20;
+ private static final int PDU_COLUMN_DATE = 21;
+ private static final int PDU_COLUMN_DELIVERY_TIME = 22;
+ private static final int PDU_COLUMN_EXPIRY = 23;
+ private static final int PDU_COLUMN_MESSAGE_SIZE = 24;
+ private static final int PDU_COLUMN_SUBJECT_CHARSET = 25;
+ private static final int PDU_COLUMN_RETRIEVE_TEXT_CHARSET = 26;
+
+ @UnsupportedAppUsage
+ private static final String[] PART_PROJECTION = new String[] {
+ Part._ID,
+ Part.CHARSET,
+ Part.CONTENT_DISPOSITION,
+ Part.CONTENT_ID,
+ Part.CONTENT_LOCATION,
+ Part.CONTENT_TYPE,
+ Part.FILENAME,
+ Part.NAME,
+ Part.TEXT
+ };
+
+ private static final int PART_COLUMN_ID = 0;
+ private static final int PART_COLUMN_CHARSET = 1;
+ private static final int PART_COLUMN_CONTENT_DISPOSITION = 2;
+ private static final int PART_COLUMN_CONTENT_ID = 3;
+ private static final int PART_COLUMN_CONTENT_LOCATION = 4;
+ private static final int PART_COLUMN_CONTENT_TYPE = 5;
+ private static final int PART_COLUMN_FILENAME = 6;
+ private static final int PART_COLUMN_NAME = 7;
+ private static final int PART_COLUMN_TEXT = 8;
+
+ @UnsupportedAppUsage
+ private static final HashMap<Uri, Integer> MESSAGE_BOX_MAP;
+ // These map are used for convenience in persist() and load().
+ private static final HashMap<Integer, Integer> CHARSET_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> ENCODED_STRING_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> TEXT_STRING_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> OCTET_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> LONG_COLUMN_INDEX_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> CHARSET_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> ENCODED_STRING_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> TEXT_STRING_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> OCTET_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> LONG_COLUMN_NAME_MAP;
+
+ static {
+ MESSAGE_BOX_MAP = new HashMap<Uri, Integer>();
+ MESSAGE_BOX_MAP.put(Mms.Inbox.CONTENT_URI, Mms.MESSAGE_BOX_INBOX);
+ MESSAGE_BOX_MAP.put(Mms.Sent.CONTENT_URI, Mms.MESSAGE_BOX_SENT);
+ MESSAGE_BOX_MAP.put(Mms.Draft.CONTENT_URI, Mms.MESSAGE_BOX_DRAFTS);
+ MESSAGE_BOX_MAP.put(Mms.Outbox.CONTENT_URI, Mms.MESSAGE_BOX_OUTBOX);
+
+ CHARSET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT_CHARSET);
+ CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT_CHARSET);
+
+ CHARSET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ CHARSET_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT_CHARSET);
+ CHARSET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT_CHARSET);
+
+ // Encoded string field code -> column index/name map.
+ ENCODED_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT);
+ ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT);
+
+ ENCODED_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT);
+ ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT);
+
+ // Text string field code -> column index/name map.
+ TEXT_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_LOCATION, PDU_COLUMN_CONTENT_LOCATION);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_TYPE, PDU_COLUMN_CONTENT_TYPE);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_CLASS, PDU_COLUMN_MESSAGE_CLASS);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_ID, PDU_COLUMN_MESSAGE_ID);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RESPONSE_TEXT, PDU_COLUMN_RESPONSE_TEXT);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.TRANSACTION_ID, PDU_COLUMN_TRANSACTION_ID);
+
+ TEXT_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_LOCATION, Mms.CONTENT_LOCATION);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_TYPE, Mms.CONTENT_TYPE);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_CLASS, Mms.MESSAGE_CLASS);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_ID, Mms.MESSAGE_ID);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.RESPONSE_TEXT, Mms.RESPONSE_TEXT);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.TRANSACTION_ID, Mms.TRANSACTION_ID);
+
+ // Octet field code -> column index/name map.
+ OCTET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_CLASS, PDU_COLUMN_CONTENT_CLASS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_REPORT, PDU_COLUMN_DELIVERY_REPORT);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_TYPE, PDU_COLUMN_MESSAGE_TYPE);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MMS_VERSION, PDU_COLUMN_MMS_VERSION);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.PRIORITY, PDU_COLUMN_PRIORITY);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_REPORT, PDU_COLUMN_READ_REPORT);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_STATUS, PDU_COLUMN_READ_STATUS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.REPORT_ALLOWED, PDU_COLUMN_REPORT_ALLOWED);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_STATUS, PDU_COLUMN_RETRIEVE_STATUS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.STATUS, PDU_COLUMN_STATUS);
+
+ OCTET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_CLASS, Mms.CONTENT_CLASS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_REPORT, Mms.DELIVERY_REPORT);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_TYPE, Mms.MESSAGE_TYPE);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.MMS_VERSION, Mms.MMS_VERSION);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.PRIORITY, Mms.PRIORITY);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_REPORT, Mms.READ_REPORT);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_STATUS, Mms.READ_STATUS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.REPORT_ALLOWED, Mms.REPORT_ALLOWED);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_STATUS, Mms.RETRIEVE_STATUS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.STATUS, Mms.STATUS);
+
+ // Long field code -> column index/name map.
+ LONG_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.DATE, PDU_COLUMN_DATE);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_TIME, PDU_COLUMN_DELIVERY_TIME);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.EXPIRY, PDU_COLUMN_EXPIRY);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_SIZE, PDU_COLUMN_MESSAGE_SIZE);
+
+ LONG_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.DATE, Mms.DATE);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_TIME, Mms.DELIVERY_TIME);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.EXPIRY, Mms.EXPIRY);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_SIZE, Mms.MESSAGE_SIZE);
+
+ PDU_CACHE_INSTANCE = PduCache.getInstance();
+ }
+
+ @UnsupportedAppUsage
+ private final Context mContext;
+ @UnsupportedAppUsage
+ private final ContentResolver mContentResolver;
+ private final DrmManagerClient mDrmManagerClient;
+ @UnsupportedAppUsage
+ private final TelephonyManager mTelephonyManager;
+
+ private PduPersister(Context context) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mDrmManagerClient = new DrmManagerClient(context);
+ mTelephonyManager = (TelephonyManager)context
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ /** Get(or create if not exist) an instance of PduPersister */
+ @UnsupportedAppUsage
+ public static PduPersister getPduPersister(Context context) {
+ if ((sPersister == null)) {
+ sPersister = new PduPersister(context);
+ } else if (!context.equals(sPersister.mContext)) {
+ sPersister.release();
+ sPersister = new PduPersister(context);
+ }
+
+ return sPersister;
+ }
+
+ private void setEncodedStringValueToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ String s = c.getString(columnIndex);
+ if ((s != null) && (s.length() > 0)) {
+ int charsetColumnIndex = CHARSET_COLUMN_INDEX_MAP.get(mapColumn);
+ int charset = c.getInt(charsetColumnIndex);
+ EncodedStringValue value = new EncodedStringValue(
+ charset, getBytes(s));
+ headers.setEncodedStringValue(value, mapColumn);
+ }
+ }
+
+ private void setTextStringToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ String s = c.getString(columnIndex);
+ if (s != null) {
+ headers.setTextString(getBytes(s), mapColumn);
+ }
+ }
+
+ private void setOctetToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) throws InvalidHeaderValueException {
+ if (!c.isNull(columnIndex)) {
+ int b = c.getInt(columnIndex);
+ headers.setOctet(b, mapColumn);
+ }
+ }
+
+ private void setLongToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ if (!c.isNull(columnIndex)) {
+ long l = c.getLong(columnIndex);
+ headers.setLongInteger(l, mapColumn);
+ }
+ }
+
+ @UnsupportedAppUsage
+ private Integer getIntegerFromPartColumn(Cursor c, int columnIndex) {
+ if (!c.isNull(columnIndex)) {
+ return c.getInt(columnIndex);
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ private byte[] getByteArrayFromPartColumn(Cursor c, int columnIndex) {
+ if (!c.isNull(columnIndex)) {
+ return getBytes(c.getString(columnIndex));
+ }
+ return null;
+ }
+
+ private PduPart[] loadParts(long msgId) throws MmsException {
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/part"),
+ PART_PROJECTION, null, null, null);
+
+ PduPart[] parts = null;
+
+ try {
+ if ((c == null) || (c.getCount() == 0)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "loadParts(" + msgId + "): no part to load.");
+ }
+ return null;
+ }
+
+ int partCount = c.getCount();
+ int partIdx = 0;
+ parts = new PduPart[partCount];
+ while (c.moveToNext()) {
+ PduPart part = new PduPart();
+ Integer charset = getIntegerFromPartColumn(
+ c, PART_COLUMN_CHARSET);
+ if (charset != null) {
+ part.setCharset(charset);
+ }
+
+ byte[] contentDisposition = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_DISPOSITION);
+ if (contentDisposition != null) {
+ part.setContentDisposition(contentDisposition);
+ }
+
+ byte[] contentId = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_ID);
+ if (contentId != null) {
+ part.setContentId(contentId);
+ }
+
+ byte[] contentLocation = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_LOCATION);
+ if (contentLocation != null) {
+ part.setContentLocation(contentLocation);
+ }
+
+ byte[] contentType = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_TYPE);
+ if (contentType != null) {
+ part.setContentType(contentType);
+ } else {
+ throw new MmsException("Content-Type must be set.");
+ }
+
+ byte[] fileName = getByteArrayFromPartColumn(
+ c, PART_COLUMN_FILENAME);
+ if (fileName != null) {
+ part.setFilename(fileName);
+ }
+
+ byte[] name = getByteArrayFromPartColumn(
+ c, PART_COLUMN_NAME);
+ if (name != null) {
+ part.setName(name);
+ }
+
+ // Construct a Uri for this part.
+ long partId = c.getLong(PART_COLUMN_ID);
+ Uri partURI = Uri.parse("content://mms/part/" + partId);
+ part.setDataUri(partURI);
+
+ // For images/audio/video, we won't keep their data in Part
+ // because their renderer accept Uri as source.
+ String type = toIsoString(contentType);
+ if (!ContentType.isImageType(type)
+ && !ContentType.isAudioType(type)
+ && !ContentType.isVideoType(type)) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ InputStream is = null;
+
+ // Store simple string values directly in the database instead of an
+ // external file. This makes the text searchable and retrieval slightly
+ // faster.
+ if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type)
+ || ContentType.TEXT_HTML.equals(type)) {
+ String text = c.getString(PART_COLUMN_TEXT);
+ byte [] blob = new EncodedStringValue(text != null ? text : "")
+ .getTextString();
+ baos.write(blob, 0, blob.length);
+ } else {
+
+ try {
+ is = mContentResolver.openInputStream(partURI);
+
+ byte[] buffer = new byte[256];
+ int len = is.read(buffer);
+ while (len >= 0) {
+ baos.write(buffer, 0, len);
+ len = is.read(buffer);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to load part data", e);
+ c.close();
+ throw new MmsException(e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close stream", e);
+ } // Ignore
+ }
+ }
+ }
+ part.setData(baos.toByteArray());
+ }
+ parts[partIdx++] = part;
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ return parts;
+ }
+
+ private void loadAddress(long msgId, PduHeaders headers) {
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/addr"),
+ new String[] { Addr.ADDRESS, Addr.CHARSET, Addr.TYPE },
+ null, null, null);
+
+ if (c != null) {
+ try {
+ while (c.moveToNext()) {
+ String addr = c.getString(0);
+ if (!TextUtils.isEmpty(addr)) {
+ int addrType = c.getInt(2);
+ switch (addrType) {
+ case PduHeaders.FROM:
+ headers.setEncodedStringValue(
+ new EncodedStringValue(c.getInt(1), getBytes(addr)),
+ addrType);
+ break;
+ case PduHeaders.TO:
+ case PduHeaders.CC:
+ case PduHeaders.BCC:
+ headers.appendEncodedStringValue(
+ new EncodedStringValue(c.getInt(1), getBytes(addr)),
+ addrType);
+ break;
+ default:
+ Log.e(TAG, "Unknown address type: " + addrType);
+ break;
+ }
+ }
+ }
+ } finally {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Load a PDU from storage by given Uri.
+ *
+ * @param uri The Uri of the PDU to be loaded.
+ * @return A generic PDU object, it may be cast to dedicated PDU.
+ * @throws MmsException Failed to load some fields of a PDU.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu load(Uri uri) throws MmsException {
+ GenericPdu pdu = null;
+ PduCacheEntry cacheEntry = null;
+ int msgBox = 0;
+ long threadId = -1;
+ try {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "load: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "load: ", e);
+ }
+ cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+ if (cacheEntry != null) {
+ return cacheEntry.getPdu();
+ }
+ }
+ // Tell the cache to indicate to other callers that this item
+ // is currently being updated.
+ PDU_CACHE_INSTANCE.setUpdating(uri, true);
+ }
+
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,
+ PDU_PROJECTION, null, null, null);
+ PduHeaders headers = new PduHeaders();
+ Set<Entry<Integer, Integer>> set;
+ long msgId = ContentUris.parseId(uri);
+
+ try {
+ if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) {
+ throw new MmsException("Bad uri: " + uri);
+ }
+
+ msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX);
+ threadId = c.getLong(PDU_COLUMN_THREAD_ID);
+
+ set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setEncodedStringValueToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setTextStringToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = OCTET_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setOctetToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = LONG_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setLongToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // Check whether 'msgId' has been assigned a valid value.
+ if (msgId == -1L) {
+ throw new MmsException("Error! ID of the message: -1.");
+ }
+
+ // Load address information of the MM.
+ loadAddress(msgId, headers);
+
+ int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
+ PduBody body = new PduBody();
+
+ // For PDU which type is M_retrieve.conf or Send.req, we should
+ // load multiparts and put them into the body of the PDU.
+ if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
+ || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
+ PduPart[] parts = loadParts(msgId);
+ if (parts != null) {
+ int partsNum = parts.length;
+ for (int i = 0; i < partsNum; i++) {
+ body.addPart(parts[i]);
+ }
+ }
+ }
+
+ switch (msgType) {
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ pdu = new NotificationInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ pdu = new DeliveryInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ pdu = new ReadOrigInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ pdu = new RetrieveConf(headers, body);
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ pdu = new SendReq(headers, body);
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ pdu = new AcknowledgeInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ pdu = new NotifyRespInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ pdu = new ReadRecInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
+ case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
+ throw new MmsException(
+ "Unsupported PDU type: " + Integer.toHexString(msgType));
+
+ default:
+ throw new MmsException(
+ "Unrecognized PDU type: " + Integer.toHexString(msgType));
+ }
+ } finally {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (pdu != null) {
+ assert(PDU_CACHE_INSTANCE.get(uri) == null);
+ // Update the cache entry with the real info
+ cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
+ PDU_CACHE_INSTANCE.put(uri, cacheEntry);
+ }
+ PDU_CACHE_INSTANCE.setUpdating(uri, false);
+ PDU_CACHE_INSTANCE.notifyAll(); // tell anybody waiting on this entry to go ahead
+ }
+ }
+ return pdu;
+ }
+
+ @UnsupportedAppUsage
+ private void persistAddress(
+ long msgId, int type, EncodedStringValue[] array) {
+ ContentValues values = new ContentValues(3);
+
+ for (EncodedStringValue addr : array) {
+ values.clear(); // Clear all values first.
+ values.put(Addr.ADDRESS, toIsoString(addr.getTextString()));
+ values.put(Addr.CHARSET, addr.getCharacterSet());
+ values.put(Addr.TYPE, type);
+
+ Uri uri = Uri.parse("content://mms/" + msgId + "/addr");
+ SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ }
+ }
+
+ @UnsupportedAppUsage
+ private static String getPartContentType(PduPart part) {
+ return part.getContentType() == null ? null : toIsoString(part.getContentType());
+ }
+
+ @UnsupportedAppUsage
+ public Uri persistPart(PduPart part, long msgId, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ Uri uri = Uri.parse("content://mms/" + msgId + "/part");
+ ContentValues values = new ContentValues(8);
+
+ int charset = part.getCharset();
+ if (charset != 0 ) {
+ values.put(Part.CHARSET, charset);
+ }
+
+ String contentType = getPartContentType(part);
+ if (contentType != null) {
+ // There is no "image/jpg" in Android (and it's an invalid mimetype).
+ // Change it to "image/jpeg"
+ if (ContentType.IMAGE_JPG.equals(contentType)) {
+ contentType = ContentType.IMAGE_JPEG;
+ }
+
+ values.put(Part.CONTENT_TYPE, contentType);
+ // To ensure the SMIL part is always the first part.
+ if (ContentType.APP_SMIL.equals(contentType)) {
+ values.put(Part.SEQ, -1);
+ }
+ } else {
+ throw new MmsException("MIME type of the part must be set.");
+ }
+
+ if (part.getFilename() != null) {
+ String fileName = new String(part.getFilename());
+ values.put(Part.FILENAME, fileName);
+ }
+
+ if (part.getName() != null) {
+ String name = new String(part.getName());
+ values.put(Part.NAME, name);
+ }
+
+ Object value = null;
+ if (part.getContentDisposition() != null) {
+ value = toIsoString(part.getContentDisposition());
+ values.put(Part.CONTENT_DISPOSITION, (String) value);
+ }
+
+ if (part.getContentId() != null) {
+ value = toIsoString(part.getContentId());
+ values.put(Part.CONTENT_ID, (String) value);
+ }
+
+ if (part.getContentLocation() != null) {
+ value = toIsoString(part.getContentLocation());
+ values.put(Part.CONTENT_LOCATION, (String) value);
+ }
+
+ Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ if (res == null) {
+ throw new MmsException("Failed to persist part, return null.");
+ }
+
+ persistData(part, res, contentType, preOpenedFiles);
+ // After successfully store the data, we should update
+ // the dataUri of the part.
+ part.setDataUri(res);
+
+ return res;
+ }
+
+ /**
+ * Save data of the part into storage. The source data may be given
+ * by a byte[] or a Uri. If it's a byte[], directly save it
+ * into storage, otherwise load source data from the dataUri and then
+ * save it. If the data is an image, we may scale down it according
+ * to user preference.
+ *
+ * @param part The PDU part which contains data to be saved.
+ * @param uri The URI of the part.
+ * @param contentType The MIME type of the part.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @throws MmsException Cannot find source data or error occurred
+ * while saving the data.
+ */
+ private void persistData(PduPart part, Uri uri,
+ String contentType, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ OutputStream os = null;
+ InputStream is = null;
+ DrmConvertSession drmConvertSession = null;
+ Uri dataUri = null;
+ String path = null;
+
+ try {
+ byte[] data = part.getData();
+ if (ContentType.TEXT_PLAIN.equals(contentType)
+ || ContentType.APP_SMIL.equals(contentType)
+ || ContentType.TEXT_HTML.equals(contentType)) {
+ ContentValues cv = new ContentValues();
+ if (data == null) {
+ data = new String("").getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
+ }
+ cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
+ if (mContentResolver.update(uri, cv, null, null) != 1) {
+ throw new MmsException("unable to update " + uri.toString());
+ }
+ } else {
+ boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
+ if (isDrm) {
+ if (uri != null) {
+ try (ParcelFileDescriptor pfd =
+ mContentResolver.openFileDescriptor(uri, "r")) {
+ if (pfd.getStatSize() > 0) {
+ // we're not going to re-persist and re-encrypt an already
+ // converted drm file
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
+ }
+ }
+ // We haven't converted the file yet, start the conversion
+ drmConvertSession = DrmConvertSession.open(mContext, contentType);
+ if (drmConvertSession == null) {
+ throw new MmsException("Mimetype " + contentType +
+ " can not be converted.");
+ }
+ }
+ // uri can look like:
+ // content://mms/part/98
+ os = mContentResolver.openOutputStream(uri);
+ if (data == null) {
+ dataUri = part.getDataUri();
+ if ((dataUri == null) || (dataUri.equals(uri))) {
+ Log.w(TAG, "Can't find data for this part.");
+ return;
+ }
+ // dataUri can look like:
+ // content://com.google.android.gallery3d.provider/picasa/item/5720646660183715586
+ if (preOpenedFiles != null && preOpenedFiles.containsKey(dataUri)) {
+ is = preOpenedFiles.get(dataUri);
+ }
+ if (is == null) {
+ is = mContentResolver.openInputStream(dataUri);
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+
+ byte[] buffer = new byte[8192];
+ for (int len = 0; (len = is.read(buffer)) != -1; ) {
+ if (!isDrm) {
+ os.write(buffer, 0, len);
+ } else {
+ byte[] convertedData = drmConvertSession.convert(buffer, len);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
+ }
+ } else {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+ if (!isDrm) {
+ os.write(data);
+ } else {
+ dataUri = uri;
+ byte[] convertedData = drmConvertSession.convert(data, data.length);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Failed to open Input/Output stream.", e);
+ throw new MmsException(e);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read/write data.", e);
+ throw new MmsException(e);
+ } finally {
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while closing: " + os, e);
+ } // Ignore
+ }
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while closing: " + is, e);
+ } // Ignore
+ }
+ if (drmConvertSession != null) {
+ drmConvertSession.close(path);
+
+ // Reset the permissions on the encrypted part file so everyone has only read
+ // permission.
+ File f = new File(path);
+ ContentValues values = new ContentValues(0);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/resetFilePerm/" + f.getName()),
+ values, null, null);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ private void updateAddress(
+ long msgId, int type, EncodedStringValue[] array) {
+ // Delete old address information and then insert new ones.
+ SqliteWrapper.delete(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/addr"),
+ Addr.TYPE + "=" + type, null);
+
+ persistAddress(msgId, type, array);
+ }
+
+ /**
+ * Update headers of a SendReq.
+ *
+ * @param uri The PDU which need to be updated.
+ * @param pdu New headers.
+ * @throws MmsException Bad URI or updating failed.
+ */
+ @UnsupportedAppUsage
+ public void updateHeaders(Uri uri, SendReq sendReq) {
+ synchronized(PDU_CACHE_INSTANCE) {
+ // If the cache item is getting updated, wait until it's done updating before
+ // purging it.
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "updateHeaders: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "updateHeaders: ", e);
+ }
+ }
+ }
+ PDU_CACHE_INSTANCE.purge(uri);
+
+ ContentValues values = new ContentValues(10);
+ byte[] contentType = sendReq.getContentType();
+ if (contentType != null) {
+ values.put(Mms.CONTENT_TYPE, toIsoString(contentType));
+ }
+
+ long date = sendReq.getDate();
+ if (date != -1) {
+ values.put(Mms.DATE, date);
+ }
+
+ int deliveryReport = sendReq.getDeliveryReport();
+ if (deliveryReport != 0) {
+ values.put(Mms.DELIVERY_REPORT, deliveryReport);
+ }
+
+ long expiry = sendReq.getExpiry();
+ if (expiry != -1) {
+ values.put(Mms.EXPIRY, expiry);
+ }
+
+ byte[] msgClass = sendReq.getMessageClass();
+ if (msgClass != null) {
+ values.put(Mms.MESSAGE_CLASS, toIsoString(msgClass));
+ }
+
+ int priority = sendReq.getPriority();
+ if (priority != 0) {
+ values.put(Mms.PRIORITY, priority);
+ }
+
+ int readReport = sendReq.getReadReport();
+ if (readReport != 0) {
+ values.put(Mms.READ_REPORT, readReport);
+ }
+
+ byte[] transId = sendReq.getTransactionId();
+ if (transId != null) {
+ values.put(Mms.TRANSACTION_ID, toIsoString(transId));
+ }
+
+ EncodedStringValue subject = sendReq.getSubject();
+ if (subject != null) {
+ values.put(Mms.SUBJECT, toIsoString(subject.getTextString()));
+ values.put(Mms.SUBJECT_CHARSET, subject.getCharacterSet());
+ } else {
+ values.put(Mms.SUBJECT, "");
+ }
+
+ long messageSize = sendReq.getMessageSize();
+ if (messageSize > 0) {
+ values.put(Mms.MESSAGE_SIZE, messageSize);
+ }
+
+ PduHeaders headers = sendReq.getPduHeaders();
+ HashSet<String> recipients = new HashSet<String>();
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = null;
+ if (addrType == PduHeaders.FROM) {
+ EncodedStringValue v = headers.getEncodedStringValue(addrType);
+ if (v != null) {
+ array = new EncodedStringValue[1];
+ array[0] = v;
+ }
+ } else {
+ array = headers.getEncodedStringValues(addrType);
+ }
+
+ if (array != null) {
+ long msgId = ContentUris.parseId(uri);
+ updateAddress(msgId, addrType, array);
+ if (addrType == PduHeaders.TO) {
+ for (EncodedStringValue v : array) {
+ if (v != null) {
+ recipients.add(v.getString());
+ }
+ }
+ }
+ }
+ }
+ if (!recipients.isEmpty()) {
+ long threadId = Threads.getOrCreateThreadId(mContext, recipients);
+ values.put(Mms.THREAD_ID, threadId);
+ }
+
+ SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
+ }
+
+ private void updatePart(Uri uri, PduPart part, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ ContentValues values = new ContentValues(7);
+
+ int charset = part.getCharset();
+ if (charset != 0 ) {
+ values.put(Part.CHARSET, charset);
+ }
+
+ String contentType = null;
+ if (part.getContentType() != null) {
+ contentType = toIsoString(part.getContentType());
+ values.put(Part.CONTENT_TYPE, contentType);
+ } else {
+ throw new MmsException("MIME type of the part must be set.");
+ }
+
+ if (part.getFilename() != null) {
+ String fileName = new String(part.getFilename());
+ values.put(Part.FILENAME, fileName);
+ }
+
+ if (part.getName() != null) {
+ String name = new String(part.getName());
+ values.put(Part.NAME, name);
+ }
+
+ Object value = null;
+ if (part.getContentDisposition() != null) {
+ value = toIsoString(part.getContentDisposition());
+ values.put(Part.CONTENT_DISPOSITION, (String) value);
+ }
+
+ if (part.getContentId() != null) {
+ value = toIsoString(part.getContentId());
+ values.put(Part.CONTENT_ID, (String) value);
+ }
+
+ if (part.getContentLocation() != null) {
+ value = toIsoString(part.getContentLocation());
+ values.put(Part.CONTENT_LOCATION, (String) value);
+ }
+
+ SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
+
+ // Only update the data when:
+ // 1. New binary data supplied or
+ // 2. The Uri of the part is different from the current one.
+ if ((part.getData() != null)
+ || (!uri.equals(part.getDataUri()))) {
+ persistData(part, uri, contentType, preOpenedFiles);
+ }
+ }
+
+ /**
+ * Update all parts of a PDU.
+ *
+ * @param uri The PDU which need to be updated.
+ * @param body New message body of the PDU.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @throws MmsException Bad URI or updating failed.
+ */
+ @UnsupportedAppUsage
+ public void updateParts(Uri uri, PduBody body, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ try {
+ PduCacheEntry cacheEntry;
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "updateParts: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "updateParts: ", e);
+ }
+ cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+ if (cacheEntry != null) {
+ ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body);
+ }
+ }
+ // Tell the cache to indicate to other callers that this item
+ // is currently being updated.
+ PDU_CACHE_INSTANCE.setUpdating(uri, true);
+ }
+
+ ArrayList<PduPart> toBeCreated = new ArrayList<PduPart>();
+ HashMap<Uri, PduPart> toBeUpdated = new HashMap<Uri, PduPart>();
+
+ int partsNum = body.getPartsNum();
+ StringBuilder filter = new StringBuilder().append('(');
+ for (int i = 0; i < partsNum; i++) {
+ PduPart part = body.getPart(i);
+ Uri partUri = part.getDataUri();
+ if ((partUri == null) || TextUtils.isEmpty(partUri.getAuthority())
+ || !partUri.getAuthority().startsWith("mms")) {
+ toBeCreated.add(part);
+ } else {
+ toBeUpdated.put(partUri, part);
+
+ // Don't use 'i > 0' to determine whether we should append
+ // 'AND' since 'i = 0' may be skipped in another branch.
+ if (filter.length() > 1) {
+ filter.append(" AND ");
+ }
+
+ filter.append(Part._ID);
+ filter.append("!=");
+ DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment());
+ }
+ }
+ filter.append(')');
+
+ long msgId = ContentUris.parseId(uri);
+
+ // Remove the parts which doesn't exist anymore.
+ SqliteWrapper.delete(mContext, mContentResolver,
+ Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"),
+ filter.length() > 2 ? filter.toString() : null, null);
+
+ // Create new parts which didn't exist before.
+ for (PduPart part : toBeCreated) {
+ persistPart(part, msgId, preOpenedFiles);
+ }
+
+ // Update the modified parts.
+ for (Map.Entry<Uri, PduPart> e : toBeUpdated.entrySet()) {
+ updatePart(e.getKey(), e.getValue(), preOpenedFiles);
+ }
+ } finally {
+ synchronized(PDU_CACHE_INSTANCE) {
+ PDU_CACHE_INSTANCE.setUpdating(uri, false);
+ PDU_CACHE_INSTANCE.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Persist a PDU object to specific location in the storage.
+ *
+ * @param pdu The PDU object to be stored.
+ * @param uri Where to store the given PDU object.
+ * @param createThreadId if true, this function may create a thread id for the recipients
+ * @param groupMmsEnabled if true, all of the recipients addressed in the PDU will be used
+ * to create the associated thread. When false, only the sender will be used in finding or
+ * creating the appropriate thread or conversation.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @return A Uri which can be used to access the stored PDU.
+ */
+
+ @UnsupportedAppUsage
+ public Uri persist(GenericPdu pdu, Uri uri, boolean createThreadId, boolean groupMmsEnabled,
+ HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ if (uri == null) {
+ throw new MmsException("Uri may not be null.");
+ }
+ long msgId = -1;
+ try {
+ msgId = ContentUris.parseId(uri);
+ } catch (NumberFormatException e) {
+ // the uri ends with "inbox" or something else like that
+ }
+ boolean existingUri = msgId != -1;
+
+ if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {
+ throw new MmsException(
+ "Bad destination, must be one of "
+ + "content://mms/inbox, content://mms/sent, "
+ + "content://mms/drafts, content://mms/outbox, "
+ + "content://mms/temp.");
+ }
+ synchronized(PDU_CACHE_INSTANCE) {
+ // If the cache item is getting updated, wait until it's done updating before
+ // purging it.
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "persist: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "persist1: ", e);
+ }
+ }
+ }
+ PDU_CACHE_INSTANCE.purge(uri);
+
+ PduHeaders header = pdu.getPduHeaders();
+ PduBody body = null;
+ ContentValues values = new ContentValues();
+ Set<Entry<Integer, String>> set;
+
+ set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set) {
+ int field = e.getKey();
+ EncodedStringValue encodedString = header.getEncodedStringValue(field);
+ if (encodedString != null) {
+ String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field);
+ values.put(e.getValue(), toIsoString(encodedString.getTextString()));
+ values.put(charsetColumn, encodedString.getCharacterSet());
+ }
+ }
+
+ set = TEXT_STRING_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ byte[] text = header.getTextString(e.getKey());
+ if (text != null) {
+ values.put(e.getValue(), toIsoString(text));
+ }
+ }
+
+ set = OCTET_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ int b = header.getOctet(e.getKey());
+ if (b != 0) {
+ values.put(e.getValue(), b);
+ }
+ }
+
+ set = LONG_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ long l = header.getLongInteger(e.getKey());
+ if (l != -1L) {
+ values.put(e.getValue(), l);
+ }
+ }
+
+ HashMap<Integer, EncodedStringValue[]> addressMap =
+ new HashMap<Integer, EncodedStringValue[]>(ADDRESS_FIELDS.length);
+ // Save address information.
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = null;
+ if (addrType == PduHeaders.FROM) {
+ EncodedStringValue v = header.getEncodedStringValue(addrType);
+ if (v != null) {
+ array = new EncodedStringValue[1];
+ array[0] = v;
+ }
+ } else {
+ array = header.getEncodedStringValues(addrType);
+ }
+ addressMap.put(addrType, array);
+ }
+
+ HashSet<String> recipients = new HashSet<String>();
+ int msgType = pdu.getMessageType();
+ // Here we only allocate thread ID for M-Notification.ind,
+ // M-Retrieve.conf and M-Send.req.
+ // Some of other PDU types may be allocated a thread ID outside
+ // this scope.
+ if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND)
+ || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
+ || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
+ switch (msgType) {
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ loadRecipients(PduHeaders.FROM, recipients, addressMap, false);
+
+ // For received messages when group MMS is enabled, we want to associate this
+ // message with the thread composed of all the recipients -- all but our own
+ // number, that is. This includes the person who sent the
+ // message or the FROM field (above) in addition to the other people the message
+ // was addressed to or the TO field. Our own number is in that TO field and
+ // we have to ignore it in loadRecipients.
+ if (groupMmsEnabled) {
+ loadRecipients(PduHeaders.TO, recipients, addressMap, true);
+
+ // Also load any numbers in the CC field to address group messaging
+ // compatibility issues with devices that place numbers in this field
+ // for group messages.
+ loadRecipients(PduHeaders.CC, recipients, addressMap, true);
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ loadRecipients(PduHeaders.TO, recipients, addressMap, false);
+ break;
+ }
+ long threadId = 0;
+ if (createThreadId && !recipients.isEmpty()) {
+ // Given all the recipients associated with this message, find (or create) the
+ // correct thread.
+ threadId = Threads.getOrCreateThreadId(mContext, recipients);
+ }
+ values.put(Mms.THREAD_ID, threadId);
+ }
+
+ // Save parts first to avoid inconsistent message is loaded
+ // while saving the parts.
+ long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
+
+ // Figure out if this PDU is a text-only message
+ boolean textOnly = true;
+
+ // Sum up the total message size
+ int messageSize = 0;
+
+ // Get body if the PDU is a RetrieveConf or SendReq.
+ if (pdu instanceof MultimediaMessagePdu) {
+ body = ((MultimediaMessagePdu) pdu).getBody();
+ // Start saving parts if necessary.
+ if (body != null) {
+ int partsNum = body.getPartsNum();
+ if (partsNum > 2) {
+ // For a text-only message there will be two parts: 1-the SMIL, 2-the text.
+ // Down a few lines below we're checking to make sure we've only got SMIL or
+ // text. We also have to check then we don't have more than two parts.
+ // Otherwise, a slideshow with two text slides would be marked as textOnly.
+ textOnly = false;
+ }
+ for (int i = 0; i < partsNum; i++) {
+ PduPart part = body.getPart(i);
+ messageSize += part.getDataLength();
+ persistPart(part, dummyId, preOpenedFiles);
+
+ // If we've got anything besides text/plain or SMIL part, then we've got
+ // an mms message with some other type of attachment.
+ String contentType = getPartContentType(part);
+ if (contentType != null && !ContentType.APP_SMIL.equals(contentType)
+ && !ContentType.TEXT_PLAIN.equals(contentType)) {
+ textOnly = false;
+ }
+ }
+ }
+ }
+ // Record whether this mms message is a simple plain text or not. This is a hint for the
+ // UI.
+ values.put(Mms.TEXT_ONLY, textOnly ? 1 : 0);
+ // The message-size might already have been inserted when parsing the
+ // PDU header. If not, then we insert the message size as well.
+ if (values.getAsInteger(Mms.MESSAGE_SIZE) == null) {
+ values.put(Mms.MESSAGE_SIZE, messageSize);
+ }
+
+ Uri res = null;
+ if (existingUri) {
+ res = uri;
+ SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);
+ } else {
+ res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ if (res == null) {
+ throw new MmsException("persist() failed: return null.");
+ }
+ // Get the real ID of the PDU and update all parts which were
+ // saved with the dummy ID.
+ msgId = ContentUris.parseId(res);
+ }
+
+ values = new ContentValues(1);
+ values.put(Part.MSG_ID, msgId);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/" + dummyId + "/part"),
+ values, null, null);
+ // We should return the longest URI of the persisted PDU, for
+ // example, if input URI is "content://mms/inbox" and the _ID of
+ // persisted PDU is '8', we should return "content://mms/inbox/8"
+ // instead of "content://mms/8".
+ // FIXME: Should the MmsProvider be responsible for this???
+ if (!existingUri) {
+ res = Uri.parse(uri + "/" + msgId);
+ }
+
+ // Save address information.
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = addressMap.get(addrType);
+ if (array != null) {
+ persistAddress(msgId, addrType, array);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * For a given address type, extract the recipients from the headers.
+ *
+ * @param addressType can be PduHeaders.FROM, PduHeaders.TO or PduHeaders.CC
+ * @param recipients a HashSet that is loaded with the recipients from the FROM, TO or CC headers
+ * @param addressMap a HashMap of the addresses from the ADDRESS_FIELDS header
+ * @param excludeMyNumber if true, the number of this phone will be excluded from recipients
+ */
+ @UnsupportedAppUsage
+ private void loadRecipients(int addressType, HashSet<String> recipients,
+ HashMap<Integer, EncodedStringValue[]> addressMap, boolean excludeMyNumber) {
+ EncodedStringValue[] array = addressMap.get(addressType);
+ if (array == null) {
+ return;
+ }
+ // If the TO recipients is only a single address, then we can skip loadRecipients when
+ // we're excluding our own number because we know that address is our own.
+ if (excludeMyNumber && array.length == 1) {
+ return;
+ }
+ final SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
+ final Set<String> myPhoneNumbers = new HashSet<String>();
+ if (excludeMyNumber) {
+ // Build a list of my phone numbers from the various sims.
+ for (int subid : subscriptionManager.getActiveSubscriptionIdList()) {
+ final String myNumber = mTelephonyManager.getLine1Number(subid);
+ if (myNumber != null) {
+ myPhoneNumbers.add(myNumber);
+ }
+ }
+ }
+
+ for (EncodedStringValue v : array) {
+ if (v != null) {
+ final String number = v.getString();
+ if (excludeMyNumber) {
+ for (final String myNumber : myPhoneNumbers) {
+ if (!PhoneNumberUtils.compare(number, myNumber)
+ && !recipients.contains(number)) {
+ // Only add numbers which aren't my own number.
+ recipients.add(number);
+ break;
+ }
+ }
+ } else if (!recipients.contains(number)){
+ recipients.add(number);
+ }
+ }
+ }
+ }
+
+ /**
+ * Move a PDU object from one location to another.
+ *
+ * @param from Specify the PDU object to be moved.
+ * @param to The destination location, should be one of the following:
+ * "content://mms/inbox", "content://mms/sent",
+ * "content://mms/drafts", "content://mms/outbox",
+ * "content://mms/trash".
+ * @return New Uri of the moved PDU.
+ * @throws MmsException Error occurred while moving the message.
+ */
+ @UnsupportedAppUsage
+ public Uri move(Uri from, Uri to) throws MmsException {
+ // Check whether the 'msgId' has been assigned a valid value.
+ long msgId = ContentUris.parseId(from);
+ if (msgId == -1L) {
+ throw new MmsException("Error! ID of the message: -1.");
+ }
+
+ // Get corresponding int value of destination box.
+ Integer msgBox = MESSAGE_BOX_MAP.get(to);
+ if (msgBox == null) {
+ throw new MmsException(
+ "Bad destination, must be one of "
+ + "content://mms/inbox, content://mms/sent, "
+ + "content://mms/drafts, content://mms/outbox, "
+ + "content://mms/temp.");
+ }
+
+ ContentValues values = new ContentValues(1);
+ values.put(Mms.MESSAGE_BOX, msgBox);
+ SqliteWrapper.update(mContext, mContentResolver, from, values, null, null);
+ return ContentUris.withAppendedId(to, msgId);
+ }
+
+ /**
+ * Wrap a byte[] into a String.
+ */
+ @UnsupportedAppUsage
+ public static String toIsoString(byte[] bytes) {
+ try {
+ return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e) {
+ // Impossible to reach here!
+ Log.e(TAG, "ISO_8859_1 must be supported!", e);
+ return "";
+ }
+ }
+
+ /**
+ * Unpack a given String into a byte[].
+ */
+ @UnsupportedAppUsage
+ public static byte[] getBytes(String data) {
+ try {
+ return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e) {
+ // Impossible to reach here!
+ Log.e(TAG, "ISO_8859_1 must be supported!", e);
+ return new byte[0];
+ }
+ }
+
+ /**
+ * Remove all objects in the temporary path.
+ */
+ public void release() {
+ Uri uri = Uri.parse(TEMPORARY_DRM_OBJECT_URI);
+ SqliteWrapper.delete(mContext, mContentResolver, uri, null, null);
+ }
+
+ /**
+ * Find all messages to be sent or downloaded before certain time.
+ */
+ @UnsupportedAppUsage
+ public Cursor getPendingMessages(long dueTime) {
+ Uri.Builder uriBuilder = PendingMessages.CONTENT_URI.buildUpon();
+ uriBuilder.appendQueryParameter("protocol", "mms");
+
+ String selection = PendingMessages.ERROR_TYPE + " < ?"
+ + " AND " + PendingMessages.DUE_TIME + " <= ?";
+
+ String[] selectionArgs = new String[] {
+ String.valueOf(MmsSms.ERR_TYPE_GENERIC_PERMANENT),
+ String.valueOf(dueTime)
+ };
+
+ return SqliteWrapper.query(mContext, mContentResolver,
+ uriBuilder.build(), null, selection, selectionArgs,
+ PendingMessages.DUE_TIME);
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java
new file mode 100644
index 0000000..9d6535c
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+
+public class QuotedPrintable {
+ private static byte ESCAPE_CHAR = '=';
+
+ /**
+ * Decodes an array quoted-printable characters into an array of original bytes.
+ * Escaped characters are converted back to their original representation.
+ *
+ * <p>
+ * This function implements a subset of
+ * quoted-printable encoding specification (rule #1 and rule #2)
+ * as defined in RFC 1521.
+ * </p>
+ *
+ * @param bytes array of quoted-printable characters
+ * @return array of original bytes,
+ * null if quoted-printable decoding is unsuccessful.
+ */
+ @UnsupportedAppUsage
+ public static final byte[] decodeQuotedPrintable(byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ for (int i = 0; i < bytes.length; i++) {
+ int b = bytes[i];
+ if (b == ESCAPE_CHAR) {
+ try {
+ if('\r' == (char)bytes[i + 1] &&
+ '\n' == (char)bytes[i + 2]) {
+ i += 2;
+ continue;
+ }
+ int u = Character.digit((char) bytes[++i], 16);
+ int l = Character.digit((char) bytes[++i], 16);
+ if (u == -1 || l == -1) {
+ return null;
+ }
+ buffer.write((char) ((u << 4) + l));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ } else {
+ buffer.write(b);
+ }
+ }
+ return buffer.toByteArray();
+ }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java
new file mode 100644
index 0000000..e38c62d
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class ReadOrigInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ public ReadOrigInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_READ_ORIG_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ ReadOrigInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-MMS-Read-status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadStatus() {
+ return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Set X-MMS-Read-status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setReadStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ */
+}
diff --git a/telephony/common/com/google/android/mms/pdu/ReadRecInd.java b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java
new file mode 100644
index 0000000..9696bc2
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class ReadRecInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-ReadRec.ind pdu.
+ *
+ * @param from the from value
+ * @param messageId the message ID value
+ * @param mmsVersion current viersion of mms
+ * @param readStatus the read status value
+ * @param to the to value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if messageId or to is null.
+ */
+ @UnsupportedAppUsage
+ public ReadRecInd(EncodedStringValue from,
+ byte[] messageId,
+ int mmsVersion,
+ int readStatus,
+ EncodedStringValue[] to) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
+ setFrom(from);
+ setMessageId(messageId);
+ setMmsVersion(mmsVersion);
+ setTo(to);
+ setReadStatus(readStatus);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ ReadRecInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-MMS-Read-status value.
+ *
+ * @return the value
+ */
+ public int getReadStatus() {
+ return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Set X-MMS-Read-status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setReadStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ */
+}
diff --git a/telephony/common/com/google/android/mms/pdu/RetrieveConf.java b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java
new file mode 100644
index 0000000..03755af
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Retrieve.conf Pdu.
+ */
+public class RetrieveConf extends MultimediaMessagePdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ @UnsupportedAppUsage
+ public RetrieveConf() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ RetrieveConf(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Constructor with given headers and body
+ *
+ * @param headers Headers for this PDU.
+ * @param body Body of this PDu.
+ */
+ @UnsupportedAppUsage
+ RetrieveConf(PduHeaders headers, PduBody body) {
+ super(headers, body);
+ }
+
+ /**
+ * Get CC value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getCc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
+ }
+
+ /**
+ * Add a "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addCc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
+ }
+
+ /**
+ * Get Content-type value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-Mms-Read-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadReport() {
+ return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Read-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReadReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Get X-Mms-Retrieve-Status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getRetrieveStatus() {
+ return mPduHeaders.getOctet(PduHeaders.RETRIEVE_STATUS);
+ }
+
+ /**
+ * Set X-Mms-Retrieve-Status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setRetrieveStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.RETRIEVE_STATUS);
+ }
+
+ /**
+ * Get X-Mms-Retrieve-Text value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getRetrieveText() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.RETRIEVE_TEXT);
+ }
+
+ /**
+ * Set X-Mms-Retrieve-Text value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setRetrieveText(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.RETRIEVE_TEXT);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getContentClass() {return 0x00;}
+ * public void setApplicId(byte value) {}
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public byte getDistributionIndicator() {return 0x00;}
+ * public void setDistributionIndicator(byte value) {}
+ *
+ * public PreviouslySentByValue getPreviouslySentBy() {return null;}
+ * public void setPreviouslySentBy(PreviouslySentByValue value) {}
+ *
+ * public PreviouslySentDateValue getPreviouslySentDate() {}
+ * public void setPreviouslySentDate(PreviouslySentDateValue value) {}
+ *
+ * public MmFlagsValue getMmFlags() {return null;}
+ * public void setMmFlags(MmFlagsValue value) {}
+ *
+ * public MmStateValue getMmState() {return null;}
+ * public void getMmState(MmStateValue value) {}
+ *
+ * public byte[] getReplaceId() {return 0x00;}
+ * public void setReplaceId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ */
+}
diff --git a/telephony/common/com/google/android/mms/pdu/SendConf.java b/telephony/common/com/google/android/mms/pdu/SendConf.java
new file mode 100644
index 0000000..b859827
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/SendConf.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 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.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class SendConf extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ @UnsupportedAppUsage
+ public SendConf() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_CONF);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ SendConf(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-Mms-Response-Status.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getResponseStatus() {
+ return mPduHeaders.getOctet(PduHeaders.RESPONSE_STATUS);
+ }
+
+ /**
+ * Set X-Mms-Response-Status.
+ *
+ * @param value the values
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setResponseStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.RESPONSE_STATUS);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getContentLocation() {return null;}
+ * public void setContentLocation(byte[] value) {}
+ *
+ * public EncodedStringValue getResponseText() {return null;}
+ * public void setResponseText(EncodedStringValue value) {}
+ *
+ * public byte getStoreStatus() {return 0x00;}
+ * public void setStoreStatus(byte value) {}
+ *
+ * public byte[] getStoreStatusText() {return null;}
+ * public void setStoreStatusText(byte[] value) {}
+ */
+}
diff --git a/telephony/common/com/google/android/mms/pdu/SendReq.java b/telephony/common/com/google/android/mms/pdu/SendReq.java
new file mode 100644
index 0000000..c1b7f93
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/SendReq.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 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.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class SendReq extends MultimediaMessagePdu {
+ private static final String TAG = "SendReq";
+
+ @UnsupportedAppUsage
+ public SendReq() {
+ super();
+
+ try {
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ setMmsVersion(PduHeaders.CURRENT_MMS_VERSION);
+ // FIXME: Content-type must be decided according to whether
+ // SMIL part present.
+ setContentType("application/vnd.wap.multipart.related".getBytes());
+ setFrom(new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()));
+ setTransactionId(generateTransactionId());
+ } catch (InvalidHeaderValueException e) {
+ // Impossible to reach here since all headers we set above are valid.
+ Log.e(TAG, "Unexpected InvalidHeaderValueException.", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private byte[] generateTransactionId() {
+ String transactionId = "T" + Long.toHexString(System.currentTimeMillis());
+ return transactionId.getBytes();
+ }
+
+ /**
+ * Constructor, used when composing a M-Send.req pdu.
+ *
+ * @param contentType the content type value
+ * @param from the from value
+ * @param mmsVersion current viersion of mms
+ * @param transactionId the transaction-id value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if contentType, form or transactionId is null.
+ */
+ public SendReq(byte[] contentType,
+ EncodedStringValue from,
+ int mmsVersion,
+ byte[] transactionId) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ setContentType(contentType);
+ setFrom(from);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ SendReq(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Constructor with given headers and body
+ *
+ * @param headers Headers for this PDU.
+ * @param body Body of this PDu.
+ */
+ @UnsupportedAppUsage
+ SendReq(PduHeaders headers, PduBody body) {
+ super(headers, body);
+ }
+
+ /**
+ * Get Bcc value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getBcc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.BCC);
+ }
+
+ /**
+ * Add a "BCC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addBcc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.BCC);
+ }
+
+ /**
+ * Set "BCC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setBcc(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.BCC);
+ }
+
+ /**
+ * Get CC value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getCc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
+ }
+
+ /**
+ * Add a "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addCc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
+ }
+
+ /**
+ * Set "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setCc(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.CC);
+ }
+
+ /**
+ * Get Content-type value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Get X-Mms-Expiry value.
+ *
+ * Expiry-value = Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getExpiry() {
+ return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Set X-Mms-Expiry value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setExpiry(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Get X-Mms-MessageSize value.
+ *
+ * Expiry-value = size of message
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getMessageSize() {
+ return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Set X-Mms-MessageSize value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setMessageSize(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Read-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadReport() {
+ return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Read-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReadReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set "To" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte getAdaptationAllowed() {return 0};
+ * public void setAdaptationAllowed(btye value) {};
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getContentClass() {return 0x00;}
+ * public void setApplicId(byte value) {}
+ *
+ * public long getDeliveryTime() {return 0};
+ * public void setDeliveryTime(long value) {};
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public MmFlagsValue getMmFlags() {return null;}
+ * public void setMmFlags(MmFlagsValue value) {}
+ *
+ * public MmStateValue getMmState() {return null;}
+ * public void getMmState(MmStateValue value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getStore() {return 0x00;}
+ * public void setStore(byte value) {}
+ */
+}
diff --git a/telephony/common/com/google/android/mms/pdu/package.html b/telephony/common/com/google/android/mms/pdu/package.html
new file mode 100755
index 0000000..c9f96a6
--- /dev/null
+++ b/telephony/common/com/google/android/mms/pdu/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/telephony/common/com/google/android/mms/util/AbstractCache.java b/telephony/common/com/google/android/mms/util/AbstractCache.java
new file mode 100644
index 0000000..ab5d48a
--- /dev/null
+++ b/telephony/common/com/google/android/mms/util/AbstractCache.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 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.google.android.mms.util;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+
+public abstract class AbstractCache<K, V> {
+ private static final String TAG = "AbstractCache";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final int MAX_CACHED_ITEMS = 500;
+
+ private final HashMap<K, CacheEntry<V>> mCacheMap;
+
+ @UnsupportedAppUsage
+ protected AbstractCache() {
+ mCacheMap = new HashMap<K, CacheEntry<V>>();
+ }
+
+ @UnsupportedAppUsage
+ public boolean put(K key, V value) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to put " + key + " into cache.");
+ }
+
+ if (mCacheMap.size() >= MAX_CACHED_ITEMS) {
+ // TODO Should remove the oldest or least hit cached entry
+ // and then cache the new one.
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Failed! size limitation reached.");
+ }
+ return false;
+ }
+
+ if (key != null) {
+ CacheEntry<V> cacheEntry = new CacheEntry<V>();
+ cacheEntry.value = value;
+ mCacheMap.put(key, cacheEntry);
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, key + " cached, " + mCacheMap.size() + " items total.");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @UnsupportedAppUsage
+ public V get(K key) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to get " + key + " from cache.");
+ }
+
+ if (key != null) {
+ CacheEntry<V> cacheEntry = mCacheMap.get(key);
+ if (cacheEntry != null) {
+ cacheEntry.hit++;
+ if (LOCAL_LOGV) {
+ Log.v(TAG, key + " hit " + cacheEntry.hit + " times.");
+ }
+ return cacheEntry.value;
+ }
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ public V purge(K key) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to purge " + key);
+ }
+
+ CacheEntry<V> v = mCacheMap.remove(key);
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, mCacheMap.size() + " items cached.");
+ }
+
+ return v != null ? v.value : null;
+ }
+
+ @UnsupportedAppUsage
+ public void purgeAll() {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purging cache, " + mCacheMap.size()
+ + " items dropped.");
+ }
+ mCacheMap.clear();
+ }
+
+ public int size() {
+ return mCacheMap.size();
+ }
+
+ private static class CacheEntry<V> {
+ int hit;
+ V value;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java
new file mode 100644
index 0000000..118de46
--- /dev/null
+++ b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 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.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class DownloadDrmHelper {
+ private static final String TAG = "DownloadDrmHelper";
+
+ /** The MIME type of special DRM files */
+ public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ /** The extensions of special DRM files */
+ public static final String EXTENSION_DRM_MESSAGE = ".dm";
+
+ public static final String EXTENSION_INTERNAL_FWDL = ".fl";
+
+ /**
+ * Checks if the Media Type is a DRM Media Type
+ *
+ * @param drmManagerClient A DrmManagerClient
+ * @param mimetype Media Type to check
+ * @return True if the Media Type is DRM else false
+ */
+ public static boolean isDrmMimeType(Context context, String mimetype) {
+ boolean result = false;
+ if (context != null) {
+ try {
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ if (drmClient != null && mimetype != null && mimetype.length() > 0) {
+ result = drmClient.canHandle("", mimetype);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the Media Type needs to be DRM converted
+ *
+ * @param mimetype Media type of the content
+ * @return True if convert is needed else false
+ */
+ @UnsupportedAppUsage
+ public static boolean isDrmConvertNeeded(String mimetype) {
+ return MIMETYPE_DRM_MESSAGE.equals(mimetype);
+ }
+
+ /**
+ * Modifies the file extension for a DRM Forward Lock file NOTE: This
+ * function shouldn't be called if the file shouldn't be DRM converted
+ */
+ @UnsupportedAppUsage
+ public static String modifyDrmFwLockFileExtension(String filename) {
+ if (filename != null) {
+ int extensionIndex;
+ extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex != -1) {
+ filename = filename.substring(0, extensionIndex);
+ }
+ filename = filename.concat(EXTENSION_INTERNAL_FWDL);
+ }
+ return filename;
+ }
+
+ /**
+ * Gets the original mime type of DRM protected content.
+ *
+ * @param context The context
+ * @param path Path to the file
+ * @param containingMime The current mime type of the file i.e. the
+ * containing mime type
+ * @return The original mime type of the file if DRM protected else the
+ * currentMime
+ */
+ public static String getOriginalMimeType(Context context, String path, String containingMime) {
+ String result = containingMime;
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ try {
+ if (drmClient.canHandle(path, null)) {
+ result = drmClient.getOriginalMimeType(path);
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG,
+ "Can't get original mime type since path is null or empty string.");
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ return result;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/util/DrmConvertSession.java b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
new file mode 100644
index 0000000..0e8ec91
--- /dev/null
+++ b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 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.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmConvertedStatus;
+import android.drm.DrmManagerClient;
+import android.provider.Downloads;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+public class DrmConvertSession {
+ private DrmManagerClient mDrmClient;
+ private int mConvertSessionId;
+ private static final String TAG = "DrmConvertSession";
+
+ private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
+ mDrmClient = drmClient;
+ mConvertSessionId = convertSessionId;
+ }
+
+ /**
+ * Start of converting a file.
+ *
+ * @param context The context of the application running the convert session.
+ * @param mimeType Mimetype of content that shall be converted.
+ * @return A convert session or null in case an error occurs.
+ */
+ @UnsupportedAppUsage
+ public static DrmConvertSession open(Context context, String mimeType) {
+ DrmManagerClient drmClient = null;
+ int convertSessionId = -1;
+ if (context != null && mimeType != null && !mimeType.equals("")) {
+ try {
+ drmClient = new DrmManagerClient(context);
+ try {
+ convertSessionId = drmClient.openConvertSession(mimeType);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Conversion of Mimetype: " + mimeType
+ + " is not supported.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not access Open DrmFramework.", e);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+
+ if (drmClient == null || convertSessionId < 0) {
+ return null;
+ } else {
+ return new DrmConvertSession(drmClient, convertSessionId);
+ }
+ }
+ /**
+ * Convert a buffer of data to protected format.
+ *
+ * @param buffer Buffer filled with data to convert.
+ * @param size The number of bytes that shall be converted.
+ * @return A Buffer filled with converted data, if execution is ok, in all
+ * other case null.
+ */
+ @UnsupportedAppUsage
+ public byte [] convert(byte[] inBuffer, int size) {
+ byte[] result = null;
+ if (inBuffer != null) {
+ DrmConvertedStatus convertedStatus = null;
+ try {
+ if (size != inBuffer.length) {
+ byte[] buf = new byte[size];
+ System.arraycopy(inBuffer, 0, buf, 0, size);
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
+ } else {
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
+ }
+
+ if (convertedStatus != null &&
+ convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
+ convertedStatus.convertedData != null) {
+ result = convertedStatus.convertedData;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
+ + mConvertSessionId, e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not convert data. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Parameter inBuffer is null");
+ }
+ return result;
+ }
+
+ /**
+ * Ends a conversion session of a file.
+ *
+ * @param fileName The filename of the converted file.
+ * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
+ * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
+ * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * occurs when accessing drm framework.
+ * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ */
+ @UnsupportedAppUsage
+ public int close(String filename) {
+ DrmConvertedStatus convertedStatus = null;
+ int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ if (mDrmClient != null && mConvertSessionId >= 0) {
+ try {
+ convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
+ if (convertedStatus == null ||
+ convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
+ convertedStatus.convertedData == null) {
+ result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ } else {
+ RandomAccessFile rndAccessFile = null;
+ try {
+ rndAccessFile = new RandomAccessFile(filename, "rw");
+ rndAccessFile.seek(convertedStatus.offset);
+ rndAccessFile.write(convertedStatus.convertedData);
+ result = Downloads.Impl.STATUS_SUCCESS;
+ } catch (FileNotFoundException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "File: " + filename + " could not be found.", e);
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not access File: " + filename + " .", e);
+ } catch (IllegalArgumentException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not open file in mode: rw", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Access to File: " + filename +
+ " was denied denied by SecurityManager.", e);
+ } finally {
+ if (rndAccessFile != null) {
+ try {
+ rndAccessFile.close();
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Failed to close File:" + filename
+ + ".", e);
+ }
+ }
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not close convertsession. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ }
+ return result;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/util/PduCache.java b/telephony/common/com/google/android/mms/util/PduCache.java
new file mode 100644
index 0000000..94e3894
--- /dev/null
+++ b/telephony/common/com/google/android/mms/util/PduCache.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 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.google.android.mms.util;
+
+import android.content.ContentUris;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.provider.Telephony.Mms;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+public final class PduCache extends AbstractCache<Uri, PduCacheEntry> {
+ private static final String TAG = "PduCache";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final int MMS_ALL = 0;
+ private static final int MMS_ALL_ID = 1;
+ private static final int MMS_INBOX = 2;
+ private static final int MMS_INBOX_ID = 3;
+ private static final int MMS_SENT = 4;
+ private static final int MMS_SENT_ID = 5;
+ private static final int MMS_DRAFTS = 6;
+ private static final int MMS_DRAFTS_ID = 7;
+ private static final int MMS_OUTBOX = 8;
+ private static final int MMS_OUTBOX_ID = 9;
+ private static final int MMS_CONVERSATION = 10;
+ private static final int MMS_CONVERSATION_ID = 11;
+
+ private static final UriMatcher URI_MATCHER;
+ private static final HashMap<Integer, Integer> MATCH_TO_MSGBOX_ID_MAP;
+
+ private static PduCache sInstance;
+
+ static {
+ URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+ URI_MATCHER.addURI("mms", null, MMS_ALL);
+ URI_MATCHER.addURI("mms", "#", MMS_ALL_ID);
+ URI_MATCHER.addURI("mms", "inbox", MMS_INBOX);
+ URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID);
+ URI_MATCHER.addURI("mms", "sent", MMS_SENT);
+ URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID);
+ URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS);
+ URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID);
+ URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX);
+ URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID);
+ URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION);
+ URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID);
+
+ MATCH_TO_MSGBOX_ID_MAP = new HashMap<Integer, Integer>();
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX);
+ }
+
+ private final HashMap<Integer, HashSet<Uri>> mMessageBoxes;
+ private final HashMap<Long, HashSet<Uri>> mThreads;
+ private final HashSet<Uri> mUpdating;
+
+ @UnsupportedAppUsage
+ private PduCache() {
+ mMessageBoxes = new HashMap<Integer, HashSet<Uri>>();
+ mThreads = new HashMap<Long, HashSet<Uri>>();
+ mUpdating = new HashSet<Uri>();
+ }
+
+ @UnsupportedAppUsage
+ synchronized public static final PduCache getInstance() {
+ if (sInstance == null) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Constructing new PduCache instance.");
+ }
+ sInstance = new PduCache();
+ }
+ return sInstance;
+ }
+
+ @Override
+ synchronized public boolean put(Uri uri, PduCacheEntry entry) {
+ int msgBoxId = entry.getMessageBox();
+ HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId);
+ if (msgBox == null) {
+ msgBox = new HashSet<Uri>();
+ mMessageBoxes.put(msgBoxId, msgBox);
+ }
+
+ long threadId = entry.getThreadId();
+ HashSet<Uri> thread = mThreads.get(threadId);
+ if (thread == null) {
+ thread = new HashSet<Uri>();
+ mThreads.put(threadId, thread);
+ }
+
+ Uri finalKey = normalizeKey(uri);
+ boolean result = super.put(finalKey, entry);
+ if (result) {
+ msgBox.add(finalKey);
+ thread.add(finalKey);
+ }
+ setUpdating(uri, false);
+ return result;
+ }
+
+ synchronized public void setUpdating(Uri uri, boolean updating) {
+ if (updating) {
+ mUpdating.add(uri);
+ } else {
+ mUpdating.remove(uri);
+ }
+ }
+
+ @UnsupportedAppUsage
+ synchronized public boolean isUpdating(Uri uri) {
+ return mUpdating.contains(uri);
+ }
+
+ @Override
+ @UnsupportedAppUsage
+ synchronized public PduCacheEntry purge(Uri uri) {
+ int match = URI_MATCHER.match(uri);
+ switch (match) {
+ case MMS_ALL_ID:
+ return purgeSingleEntry(uri);
+ case MMS_INBOX_ID:
+ case MMS_SENT_ID:
+ case MMS_DRAFTS_ID:
+ case MMS_OUTBOX_ID:
+ String msgId = uri.getLastPathSegment();
+ return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId));
+ // Implicit batch of purge, return null.
+ case MMS_ALL:
+ case MMS_CONVERSATION:
+ purgeAll();
+ return null;
+ case MMS_INBOX:
+ case MMS_SENT:
+ case MMS_DRAFTS:
+ case MMS_OUTBOX:
+ purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match));
+ return null;
+ case MMS_CONVERSATION_ID:
+ purgeByThreadId(ContentUris.parseId(uri));
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ private PduCacheEntry purgeSingleEntry(Uri key) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromThreads(key, entry);
+ removeFromMessageBoxes(key, entry);
+ return entry;
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ @Override
+ synchronized public void purgeAll() {
+ super.purgeAll();
+
+ mMessageBoxes.clear();
+ mThreads.clear();
+ mUpdating.clear();
+ }
+
+ /**
+ * @param uri The Uri to be normalized.
+ * @return Uri The normalized key of cached entry.
+ */
+ private Uri normalizeKey(Uri uri) {
+ int match = URI_MATCHER.match(uri);
+ Uri normalizedKey = null;
+
+ switch (match) {
+ case MMS_ALL_ID:
+ normalizedKey = uri;
+ break;
+ case MMS_INBOX_ID:
+ case MMS_SENT_ID:
+ case MMS_DRAFTS_ID:
+ case MMS_OUTBOX_ID:
+ String msgId = uri.getLastPathSegment();
+ normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId);
+ break;
+ default:
+ return null;
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, uri + " -> " + normalizedKey);
+ }
+ return normalizedKey;
+ }
+
+ private void purgeByMessageBox(Integer msgBoxId) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purge cache in message box: " + msgBoxId);
+ }
+
+ if (msgBoxId != null) {
+ HashSet<Uri> msgBox = mMessageBoxes.remove(msgBoxId);
+ if (msgBox != null) {
+ for (Uri key : msgBox) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromThreads(key, entry);
+ }
+ }
+ }
+ }
+ }
+
+ private void removeFromThreads(Uri key, PduCacheEntry entry) {
+ HashSet<Uri> thread = mThreads.get(entry.getThreadId());
+ if (thread != null) {
+ thread.remove(key);
+ }
+ }
+
+ private void purgeByThreadId(long threadId) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purge cache in thread: " + threadId);
+ }
+
+ HashSet<Uri> thread = mThreads.remove(threadId);
+ if (thread != null) {
+ for (Uri key : thread) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromMessageBoxes(key, entry);
+ }
+ }
+ }
+ }
+
+ private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) {
+ HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox()));
+ if (msgBox != null) {
+ msgBox.remove(key);
+ }
+ }
+}
diff --git a/telephony/common/com/google/android/mms/util/PduCacheEntry.java b/telephony/common/com/google/android/mms/util/PduCacheEntry.java
new file mode 100644
index 0000000..1ecd1bf
--- /dev/null
+++ b/telephony/common/com/google/android/mms/util/PduCacheEntry.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 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.google.android.mms.util;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.pdu.GenericPdu;
+
+public final class PduCacheEntry {
+ private final GenericPdu mPdu;
+ private final int mMessageBox;
+ private final long mThreadId;
+
+ @UnsupportedAppUsage
+ public PduCacheEntry(GenericPdu pdu, int msgBox, long threadId) {
+ mPdu = pdu;
+ mMessageBox = msgBox;
+ mThreadId = threadId;
+ }
+
+ @UnsupportedAppUsage
+ public GenericPdu getPdu() {
+ return mPdu;
+ }
+
+ @UnsupportedAppUsage
+ public int getMessageBox() {
+ return mMessageBox;
+ }
+
+ @UnsupportedAppUsage
+ public long getThreadId() {
+ return mThreadId;
+ }
+}
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
new file mode 100644
index 0000000..2dd1dc1
--- /dev/null
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 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.google.android.mms.util;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public final class SqliteWrapper {
+ private static final String TAG = "SqliteWrapper";
+ private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
+ = "unable to open database file";
+
+ private SqliteWrapper() {
+ // Forbidden being instantiated.
+ }
+
+ // FIXME: It looks like outInfo.lowMemory does not work well as we expected.
+ // after run command: adb shell fillup -p 100, outInfo.lowMemory is still false.
+ private static boolean isLowMemory(Context context) {
+ if (null == context) {
+ return false;
+ }
+
+ ActivityManager am = (ActivityManager)
+ context.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
+ am.getMemoryInfo(outInfo);
+
+ return outInfo.lowMemory;
+ }
+
+ // FIXME: need to optimize this method.
+ private static boolean isLowMemory(SQLiteException e) {
+ return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
+ }
+
+ @UnsupportedAppUsage
+ public static void checkSQLiteException(Context context, SQLiteException e) {
+ if (isLowMemory(e)) {
+ Toast.makeText(context, com.android.internal.R.string.low_memory,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ throw e;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static Cursor query(Context context, ContentResolver resolver, Uri uri,
+ String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ try {
+ return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when query: ", e);
+ checkSQLiteException(context, e);
+ return null;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static boolean requery(Context context, Cursor cursor) {
+ try {
+ return cursor.requery();
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when requery: ", e);
+ checkSQLiteException(context, e);
+ return false;
+ }
+ }
+ @UnsupportedAppUsage
+ public static int update(Context context, ContentResolver resolver, Uri uri,
+ ContentValues values, String where, String[] selectionArgs) {
+ try {
+ return resolver.update(uri, values, where, selectionArgs);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when update: ", e);
+ checkSQLiteException(context, e);
+ return -1;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static int delete(Context context, ContentResolver resolver, Uri uri,
+ String where, String[] selectionArgs) {
+ try {
+ return resolver.delete(uri, where, selectionArgs);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when delete: ", e);
+ checkSQLiteException(context, e);
+ return -1;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static Uri insert(Context context, ContentResolver resolver,
+ Uri uri, ContentValues values) {
+ try {
+ return resolver.insert(uri, values);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when insert: ", e);
+ checkSQLiteException(context, e);
+ return null;
+ }
+ }
+}
diff --git a/telephony/common/com/google/android/mms/util/package.html b/telephony/common/com/google/android/mms/util/package.html
new file mode 100755
index 0000000..c9f96a6
--- /dev/null
+++ b/telephony/common/com/google/android/mms/util/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index d5e447e..25babe5 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -17,6 +17,8 @@
package android.telephony;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -64,14 +66,14 @@
* @param slotIndex the index of the slot which received the message
* @param message the SMS PDU
*/
- public abstract void onGsmCellBroadcastSms(int slotIndex, byte[] message);
+ public abstract void onGsmCellBroadcastSms(int slotIndex, @NonNull byte[] message);
/**
* Handle a CDMA cell broadcast SMS message forwarded from the system.
* @param slotIndex the index of the slot which received the message
* @param message the SMS PDU
*/
- public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] message);
+ public abstract void onCdmaCellBroadcastSms(int slotIndex, @NonNull byte[] message);
/**
* If overriding this method, call through to the super method for any unknown actions.
@@ -79,7 +81,8 @@
*/
@Override
@CallSuper
- public IBinder onBind(Intent intent) {
+ @NonNull
+ public IBinder onBind(@Nullable Intent intent) {
return mStubWrapper;
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index cdf01da..4dc54f0 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -245,9 +245,12 @@
}
/**
- * Get the Ec/No as dB
+ * Get the Ec/No (Energy per chip over the noise spectral density) as dB.
*
- * @hide
+ * Reference: TS 25.133 Section 9.1.2.3
+ *
+ * @return the Ec/No of the measured cell in the range [-24, 1] or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable
*/
public int getEcNo() {
return mEcNo;
diff --git a/telephony/java/android/telephony/MmsManager.java b/telephony/java/android/telephony/MmsManager.java
new file mode 100644
index 0000000..4bcf046
--- /dev/null
+++ b/telephony/java/android/telephony/MmsManager.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 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.app.ActivityThread;
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.IMms;
+
+/**
+ * Manages MMS operations such as sending multimedia messages.
+ * Get this object by calling the static method {@link #getInstance()}.
+ * @hide
+ */
+public class MmsManager {
+ private static final String TAG = "MmsManager";
+
+ /** Singleton object constructed during class initialization. */
+ private static final MmsManager sInstance = new MmsManager();
+
+ /**
+ * Get the MmsManager singleton instance.
+ *
+ * @return the {@link MmsManager} singleton instance.
+ */
+ public static MmsManager getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * Send an MMS message
+ *
+ * @param subId the subscription id
+ * @param contentUri the content Uri from which the message pdu will be read
+ * @param locationUrl the optional location url where message should be sent to
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * sending the message.
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message
+ * is successfully sent, or failed
+ */
+ public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl,
+ Bundle configOverrides, PendingIntent sentIntent) {
+ try {
+ final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms == null) {
+ return;
+ }
+
+ iMms.sendMessage(subId, ActivityThread.currentPackageName(), contentUri,
+ locationUrl, configOverrides, sentIntent);
+ } catch (RemoteException e) {
+ // Ignore it
+ }
+ }
+
+ /**
+ * Download an MMS message from carrier by a given location URL
+ *
+ * @param subId the subscription id
+ * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+ * from the MMS WAP push notification
+ * @param contentUri the content uri to which the downloaded pdu will be written
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * downloading the message.
+ * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is downloaded, or the download is failed
+ * @throws IllegalArgumentException if locationUrl or contentUri is empty
+ */
+ public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri,
+ Bundle configOverrides, PendingIntent downloadedIntent) {
+ try {
+ final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms == null) {
+ return;
+ }
+ iMms.downloadMessage(subId, ActivityThread.currentPackageName(),
+ locationUrl, contentUri, configOverrides, downloadedIntent);
+ } catch (RemoteException e) {
+ // Ignore it
+ }
+ }
+
+ /**
+ * Get carrier-dependent configuration values.
+ *
+ * @param subId the subscription id
+ * @return bundle key/values pairs of configuration values
+ */
+ public Bundle getCarrierConfigValues(int subId) {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.getCarrierConfigValues(subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
+ }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index f661bba..3157e12 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -27,13 +27,11 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.PendingIntent;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.CursorWindow;
import android.net.Uri;
-import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -45,7 +43,6 @@
import android.util.Log;
import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.IMms;
import com.android.internal.telephony.ISms;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.SmsRawData;
@@ -357,6 +354,68 @@
true /* persistMessage*/, ActivityThread.currentPackageName());
}
+ /**
+ * Send a text based SMS with messaging options.
+ *
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ * {@hide}
+ */
+ @UnsupportedAppUsage
+ public void sendTextMessage(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ true /* persistMessage*/, priority, expectMore, validityPeriod);
+ }
+
private void sendTextMessageInternal(String destinationAddress, String scAddress,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessage, String packageName) {
@@ -455,109 +514,6 @@
false /* persistMessage */, ActivityThread.currentPackageName());
}
- /**
- * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is
- * for internal use only.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the SMS being sent on the subscription associated with logical
- * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
- * correct subscription.
- * </p>
- *
- * @param persistMessage whether to persist the sent message in the SMS app. the caller must be
- * the Phone process if set to false.
- *
- * @hide
- */
- public void sendTextMessageWithSelfPermissions(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) {
- if (TextUtils.isEmpty(destinationAddress)) {
- throw new IllegalArgumentException("Invalid destinationAddress");
- }
-
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Invalid message body");
- }
-
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendTextForSubscriberWithSelfPermissions(getSubscriptionId(),
- ActivityThread.currentPackageName(),
- destinationAddress,
- scAddress, text, sentIntent, deliveryIntent, persistMessage);
- } catch (RemoteException ex) {
- notifySmsGenericError(sentIntent);
- }
- }
-
- /**
- * Send a text based SMS with messaging options.
- *
- * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
- * manager on a multi-SIM device, this operation may fail sending the SMS message because no
- * suitable default subscription could be found. In this case, if {@code sentIntent} is
- * non-null, then the {@link PendingIntent} will be sent with an error code
- * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
- * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
- * where this operation may fail.
- * </p>
- *
- * @param destinationAddress the address to send the message to
- * @param scAddress is the service center address or null to use
- * the current default SMSC
- * @param text the body of the message to send
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is delivered to the recipient. The
- * raw pdu of the status report is in the extended data ("pdu").
- * @param priority Priority level of the message
- * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
- * ---------------------------------
- * PRIORITY | Level of Priority
- * ---------------------------------
- * '00' | Normal
- * '01' | Interactive
- * '10' | Urgent
- * '11' | Emergency
- * ----------------------------------
- * Any Other values included Negative considered as Invalid Priority Indicator of the message.
- * @param expectMore is a boolean to indicate the sending messages through same link or not.
- * @param validityPeriod Validity Period of the message in mins.
- * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * Validity Period(Minimum) -> 5 mins
- * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
- * Any Other values included Negative considered as Invalid Validity Period of the message.
- *
- * @throws IllegalArgumentException if destinationAddress or text are empty
- * {@hide}
- */
- @UnsupportedAppUsage
- public void sendTextMessage(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent,
- int priority, boolean expectMore, int validityPeriod) {
- sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- true /* persistMessage*/, priority, expectMore, validityPeriod);
- }
-
private void sendTextMessageInternal(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
@@ -1078,37 +1034,6 @@
}
/**
- * Send a multi-part text based SMS without writing it into the SMS Provider.
- *
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
- * privileges.
- * </p>
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony
- * framework and will never trigger an SMS disambiguation dialog. If this method is called on a
- * device that has multiple active subscriptions, this {@link SmsManager} instance has been
- * created with {@link #getDefault()}, and no user-defined default subscription is defined, the
- * subscription ID associated with this message will be INVALID, which will result in the SMS
- * being sent on the subscription associated with logical slot 0. Use
- * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct
- * subscription.
- * </p>
- *
- * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList,
- * ArrayList, int, boolean, int)
- * @hide
- **/
- public void sendMultipartTextMessageWithoutPersisting(
- String destinationAddress, String scAddress, List<String> parts,
- List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
- int priority, boolean expectMore, int validityPeriod) {
- sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
- deliveryIntents, false /* persistMessage*/, priority, expectMore,
- validityPeriod);
- }
-
- /**
* Send a data based SMS to a specific application port.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -1180,45 +1105,6 @@
}
/**
- * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is
- * for internal use only.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the SMS being sent on the subscription associated with logical
- * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
- * correct subscription.
- * </p>
- *
- * @hide
- */
- public void sendDataMessageWithSelfPermissions(
- String destinationAddress, String scAddress, short destinationPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- if (TextUtils.isEmpty(destinationAddress)) {
- throw new IllegalArgumentException("Invalid destinationAddress");
- }
-
- if (data == null || data.length == 0) {
- throw new IllegalArgumentException("Invalid message data");
- }
-
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress, scAddress,
- destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
- } catch (RemoteException e) {
- Log.e(TAG, "sendDataMessageWithSelfPermissions: Couldn't send SMS - Exception: "
- + e.getMessage());
- notifySmsGenericError(sentIntent);
- }
- }
-
- /**
* Get the SmsManager associated with the default subscription id. The instance will always be
* associated with the default subscription id, even if the default subscription id changes.
*
@@ -1650,100 +1536,6 @@
/**
* Enable reception of cell broadcast (SMS-CB) messages with the given
- * message identifier and RAN type. The RAN type specify this message ID
- * belong to 3GPP (GSM) or 3GPP2(CDMA).Note that if two different clients
- * enable the same message identifier, they must both disable it for the device to stop
- * receiving those messages. All received messages will be broadcast in an
- * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
- * Note: This call is blocking, callers may want to avoid calling it from
- * the main thread of an application.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
- * </p>
- *
- * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
- * or C.R1001-G (3GPP2)
- * @param ranType the message format as defined in {@link SmsCbMessage]
- * @return true if successful, false otherwise
- * @see #disableCellBroadcast(int, int)
- *
- * {@hide}
- */
- public boolean enableCellBroadcast(int messageIdentifier,
- @android.telephony.SmsCbMessage.MessageFormat int ranType) {
- boolean success = false;
-
- try {
- ISms iSms = getISmsService();
- if (iSms != null) {
- // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
- // the default phone internally.
- success = iSms.enableCellBroadcastForSubscriber(getSubscriptionId(),
- messageIdentifier, ranType);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
-
- return success;
- }
-
- /**
- * Disable reception of cell broadcast (SMS-CB) messages with the given
- * message identifier and RAN type. The RAN type specify this message ID
- * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients
- * enable the same message identifier, they must both disable it for the
- * device to stop receiving those messages.
- * Note: This call is blocking, callers may want to avoid calling it from
- * the main thread of an application.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
- * </p>
- *
- * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
- * or C.R1001-G (3GPP2)
- * @param ranType the message format as defined in {@link SmsCbMessage}
- * @return true if successful, false otherwise
- *
- * @see #enableCellBroadcast(int, int)
- *
- * {@hide}
- */
- public boolean disableCellBroadcast(int messageIdentifier,
- @android.telephony.SmsCbMessage.MessageFormat int ranType) {
- boolean success = false;
-
- try {
- ISms iSms = getISmsService();
- if (iSms != null) {
- // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
- // the default phone internally.
- success = iSms.disableCellBroadcastForSubscriber(getSubscriptionId(),
- messageIdentifier, ranType);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
-
- return success;
- }
-
- /**
- * Enable reception of cell broadcast (SMS-CB) messages with the given
* message identifier range and RAN type. The RAN type specifies if this message ID
* belongs to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients enable
* the same message identifier, they must both disable it for the device to stop
@@ -2174,9 +1966,6 @@
@SystemApi
static public final int RESULT_REQUEST_NOT_SUPPORTED = 24;
-
- static private final String PHONE_PACKAGE_NAME = "com.android.phone";
-
/**
* Send an MMS message
*
@@ -2203,17 +1992,8 @@
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- try {
- final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms == null) {
- return;
- }
-
- iMms.sendMessage(getSubscriptionId(), ActivityThread.currentPackageName(), contentUri,
+ MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri,
locationUrl, configOverrides, sentIntent);
- } catch (RemoteException e) {
- // Ignore it
- }
}
/**
@@ -2246,16 +2026,8 @@
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- try {
- final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms == null) {
- return;
- }
- iMms.downloadMessage(getSubscriptionId(), ActivityThread.currentPackageName(),
- locationUrl, contentUri, configOverrides, downloadedIntent);
- } catch (RemoteException e) {
- // Ignore it
- }
+ MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl,
+ contentUri, configOverrides, downloadedIntent);
}
// MMS send/download failure result codes
@@ -2273,434 +2045,17 @@
/** Intent extra name for HTTP status code for MMS HTTP failure in integer type */
public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS";
- /**
- * Import a text message into system's SMS store
- *
- * Only default SMS apps can import SMS
- *
- * @param address the destination(source) address of the sent(received) message
- * @param type the type of the message
- * @param text the message text
- * @param timestampMillis the message timestamp in milliseconds
- * @param seen if the message is seen
- * @param read if the message is read
- * @return the message URI, null if failed
- * @hide
- */
- public Uri importTextMessage(String address, int type, String text, long timestampMillis,
- boolean seen, boolean read) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.importTextMessage(ActivityThread.currentPackageName(),
- address, type, text, timestampMillis, seen, read);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
/** Represents the received SMS message for importing {@hide} */
public static final int SMS_TYPE_INCOMING = 0;
/** Represents the sent SMS message for importing {@hide} */
public static final int SMS_TYPE_OUTGOING = 1;
- /**
- * Import a multimedia message into system's MMS store. Only the following PDU type is
- * supported: Retrieve.conf, Send.req, Notification.ind, Delivery.ind, Read-Orig.ind
- *
- * Only default SMS apps can import MMS
- *
- * @param contentUri the content uri from which to read the PDU of the message to import
- * @param messageId the optional message id. Use null if not specifying
- * @param timestampSecs the optional message timestamp. Use -1 if not specifying
- * @param seen if the message is seen
- * @param read if the message is read
- * @return the message URI, null if failed
- * @throws IllegalArgumentException if pdu is empty
- * {@hide}
- */
- public Uri importMultimediaMessage(Uri contentUri, String messageId, long timestampSecs,
- boolean seen, boolean read) {
- if (contentUri == null) {
- throw new IllegalArgumentException("Uri contentUri null");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.importMultimediaMessage(ActivityThread.currentPackageName(),
- contentUri, messageId, timestampSecs, seen, read);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
- /**
- * Delete a system stored SMS or MMS message
- *
- * Only default SMS apps can delete system stored SMS and MMS messages
- *
- * @param messageUri the URI of the stored message
- * @return true if deletion is successful, false otherwise
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public boolean deleteStoredMessage(Uri messageUri) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.deleteStoredMessage(ActivityThread.currentPackageName(), messageUri);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
- * Delete a system stored SMS or MMS thread
- *
- * Only default SMS apps can delete system stored SMS and MMS conversations
- *
- * @param conversationId the ID of the message conversation
- * @return true if deletion is successful, false otherwise
- * {@hide}
- */
- public boolean deleteStoredConversation(long conversationId) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.deleteStoredConversation(
- ActivityThread.currentPackageName(), conversationId);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
- * Update the status properties of a system stored SMS or MMS message, e.g.
- * the read status of a message, etc.
- *
- * @param messageUri the URI of the stored message
- * @param statusValues a list of status properties in key-value pairs to update
- * @return true if update is successful, false otherwise
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public boolean updateStoredMessageStatus(Uri messageUri, ContentValues statusValues) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.updateStoredMessageStatus(ActivityThread.currentPackageName(),
- messageUri, statusValues);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
/** Message status property: whether the message has been seen. 1 means seen, 0 not {@hide} */
public static final String MESSAGE_STATUS_SEEN = "seen";
/** Message status property: whether the message has been read. 1 means read, 0 not {@hide} */
public static final String MESSAGE_STATUS_READ = "read";
/**
- * Archive or unarchive a stored conversation
- *
- * @param conversationId the ID of the message conversation
- * @param archived true to archive the conversation, false to unarchive
- * @return true if update is successful, false otherwise
- * {@hide}
- */
- public boolean archiveStoredConversation(long conversationId, boolean archived) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.archiveStoredConversation(ActivityThread.currentPackageName(),
- conversationId, archived);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
- * Add a text message draft to system SMS store
- *
- * Only default SMS apps can add SMS draft
- *
- * @param address the destination address of message
- * @param text the body of the message to send
- * @return the URI of the stored draft message
- * {@hide}
- */
- public Uri addTextMessageDraft(String address, String text) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.addTextMessageDraft(ActivityThread.currentPackageName(), address, text);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
- /**
- * Add a multimedia message draft to system MMS store
- *
- * Only default SMS apps can add MMS draft
- *
- * @param contentUri the content uri from which to read the PDU data of the draft MMS
- * @return the URI of the stored draft message
- * @throws IllegalArgumentException if pdu is empty
- * {@hide}
- */
- public Uri addMultimediaMessageDraft(Uri contentUri) {
- if (contentUri == null) {
- throw new IllegalArgumentException("Uri contentUri null");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.addMultimediaMessageDraft(ActivityThread.currentPackageName(),
- contentUri);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
- /**
- * Send a system stored text message.
- *
- * You can only send a failed text message or a draft text message.
- *
- * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
- * manager on a multi-SIM device, this operation may fail sending the SMS message because no
- * suitable default subscription could be found. In this case, if {@code sentIntent} is
- * non-null, then the {@link PendingIntent} will be sent with an error code
- * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
- * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
- * where this operation may fail.
- * </p>
- *
- * @param messageUri the URI of the stored message
- * @param scAddress is the service center address or null to use the current default SMSC
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is delivered to the recipient. The
- * raw pdu of the status report is in the extended data ("pdu").
- *
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public void sendStoredTextMessage(Uri messageUri, String scAddress, PendingIntent sentIntent,
- PendingIntent deliveryIntent) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- final Context context = ActivityThread.currentApplication().getApplicationContext();
- resolveSubscriptionForOperation(new SubscriptionResolverResult() {
- @Override
- public void onSuccess(int subId) {
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendStoredText(subId, ActivityThread.currentPackageName(), messageUri,
- scAddress, sentIntent, deliveryIntent);
- } catch (RemoteException e) {
- Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: "
- + e.getMessage());
- notifySmsGenericError(sentIntent);
- }
- }
- @Override
- public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntent);
- }
- });
- }
-
- /**
- * Send a system stored multi-part text message.
- *
- * You can only send a failed text message or a draft text message.
- * The provided <code>PendingIntent</code> lists should match the part number of the
- * divided text of the stored message by using <code>divideMessage</code>
- *
- * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
- * manager on a multi-SIM device, this operation may fail sending the SMS message because no
- * suitable default subscription could be found. In this case, if {@code sentIntent} is
- * non-null, then the {@link PendingIntent} will be sent with an error code
- * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
- * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
- * where this operation may fail.
- * </p>
- *
- * @param messageUri the URI of the stored message
- * @param scAddress is the service center address or null to use
- * the current default SMSC
- * @param sentIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been sent.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been delivered
- * to the recipient. The raw pdu of the status report is in the
- * extended data ("pdu").
- *
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public void sendStoredMultipartTextMessage(Uri messageUri, String scAddress,
- ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- final Context context = ActivityThread.currentApplication().getApplicationContext();
- resolveSubscriptionForOperation(new SubscriptionResolverResult() {
- @Override
- public void onSuccess(int subId) {
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendStoredMultipartText(subId, ActivityThread.currentPackageName(),
- messageUri, scAddress, sentIntents, deliveryIntents);
- } catch (RemoteException e) {
- Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: "
- + e.getMessage());
- notifySmsGenericError(sentIntents);
- }
- }
- @Override
- public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntents);
- }
- });
- }
-
- /**
- * Send a system stored MMS message
- *
- * This is used for sending a previously sent, but failed-to-send, message or
- * for sending a text message that has been stored as a draft.
- *
- * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
- * </p>
- *
- * @param messageUri the URI of the stored message
- * @param configOverrides the carrier-specific messaging configuration values to override for
- * sending the message.
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public void sendStoredMultimediaMessage(Uri messageUri, Bundle configOverrides,
- PendingIntent sentIntent) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- iMms.sendStoredMessage(
- getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
- configOverrides, sentIntent);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- }
-
- /**
- * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system
- *
- * When this flag is on, all SMS/MMS sent/received are stored by system automatically
- * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
- * automatically
- *
- * This flag can only be changed by default SMS apps
- *
- * @param enabled Whether to enable message auto persisting
- * {@hide}
- */
- public void setAutoPersisting(boolean enabled) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- iMms.setAutoPersisting(ActivityThread.currentPackageName(), enabled);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- }
-
- /**
- * Get the value of the flag to automatically write sent/received SMS/MMS messages into system
- *
- * When this flag is on, all SMS/MMS sent/received are stored by system automatically
- * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
- * automatically
- *
- * @return the current value of the auto persist flag
- * {@hide}
- */
- public boolean getAutoPersisting() {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.getAutoPersisting();
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
* Get carrier-dependent configuration values.
*
* <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
@@ -2716,15 +2071,7 @@
* @return bundle key/values pairs of configuration values
*/
public Bundle getCarrierConfigValues() {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.getCarrierConfigValues(getSubscriptionId());
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
+ return MmsManager.getInstance().getCarrierConfigValues(getSubscriptionId());
}
/**
@@ -2810,38 +2157,38 @@
}
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* The prefixes is a list of prefix {@code String} separated by this delimiter.
* @hide
*/
public static final String REGEX_PREFIX_DELIMITER = ",";
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* The success status to be added into the intent to be sent to the calling package.
* @hide
*/
public static final int RESULT_STATUS_SUCCESS = 0;
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* The timeout status to be added into the intent to be sent to the calling package.
* @hide
*/
public static final int RESULT_STATUS_TIMEOUT = 1;
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* Intent extra key of the retrieved SMS message as a {@code String}.
* @hide
*/
public static final String EXTRA_SMS_MESSAGE = "android.telephony.extra.SMS_MESSAGE";
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* Intent extra key of SMS retriever status, which indicates whether the request for the
* coming SMS message is SUCCESS or TIMEOUT
* @hide
*/
public static final String EXTRA_STATUS = "android.telephony.extra.STATUS";
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* [Optional] Intent extra key of the retrieved Sim card subscription Id if any. {@code int}
* @hide
*/
@@ -2891,74 +2238,6 @@
}
}
- /**
- * Filters a bundle to only contain MMS config variables.
- *
- * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS
- * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the
- * supplied bundle.
- *
- * @param config a Bundle that contains MMS config variables and possibly more.
- * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above.
- * @hide
- */
- public static Bundle getMmsConfig(BaseBundle config) {
- Bundle filtered = new Bundle();
- filtered.putBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID,
- config.getBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID));
- filtered.putBoolean(MMS_CONFIG_MMS_ENABLED, config.getBoolean(MMS_CONFIG_MMS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_GROUP_MMS_ENABLED,
- config.getBoolean(MMS_CONFIG_GROUP_MMS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED,
- config.getBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED));
- filtered.putBoolean(MMS_CONFIG_ALIAS_ENABLED, config.getBoolean(MMS_CONFIG_ALIAS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO,
- config.getBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO));
- filtered.putBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED,
- config.getBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED,
- config.getBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED));
- filtered.putBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION,
- config.getBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION));
- filtered.putBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES,
- config.getBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES));
- filtered.putBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED,
- config.getBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED));
- filtered.putBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED,
- config.getBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED));
- filtered.putBoolean(MMS_CONFIG_CLOSE_CONNECTION,
- config.getBoolean(MMS_CONFIG_CLOSE_CONNECTION));
- filtered.putInt(MMS_CONFIG_MAX_MESSAGE_SIZE, config.getInt(MMS_CONFIG_MAX_MESSAGE_SIZE));
- filtered.putInt(MMS_CONFIG_MAX_IMAGE_WIDTH, config.getInt(MMS_CONFIG_MAX_IMAGE_WIDTH));
- filtered.putInt(MMS_CONFIG_MAX_IMAGE_HEIGHT, config.getInt(MMS_CONFIG_MAX_IMAGE_HEIGHT));
- filtered.putInt(MMS_CONFIG_RECIPIENT_LIMIT, config.getInt(MMS_CONFIG_RECIPIENT_LIMIT));
- filtered.putInt(MMS_CONFIG_ALIAS_MIN_CHARS, config.getInt(MMS_CONFIG_ALIAS_MIN_CHARS));
- filtered.putInt(MMS_CONFIG_ALIAS_MAX_CHARS, config.getInt(MMS_CONFIG_ALIAS_MAX_CHARS));
- filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD,
- config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD));
- filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD,
- config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD));
- filtered.putInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE,
- config.getInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE));
- filtered.putInt(MMS_CONFIG_SUBJECT_MAX_LENGTH,
- config.getInt(MMS_CONFIG_SUBJECT_MAX_LENGTH));
- filtered.putInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT,
- config.getInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
- filtered.putString(MMS_CONFIG_UA_PROF_TAG_NAME,
- config.getString(MMS_CONFIG_UA_PROF_TAG_NAME));
- filtered.putString(MMS_CONFIG_USER_AGENT, config.getString(MMS_CONFIG_USER_AGENT));
- filtered.putString(MMS_CONFIG_UA_PROF_URL, config.getString(MMS_CONFIG_UA_PROF_URL));
- filtered.putString(MMS_CONFIG_HTTP_PARAMS, config.getString(MMS_CONFIG_HTTP_PARAMS));
- filtered.putString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER,
- config.getString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER));
- filtered.putString(MMS_CONFIG_NAI_SUFFIX, config.getString(MMS_CONFIG_NAI_SUFFIX));
- filtered.putBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS,
- config.getBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS));
- filtered.putBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER,
- config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
- return filtered;
- }
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"SMS_CATEGORY_"},
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c9440a6..d2559e7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9448,16 +9448,28 @@
return returnValue;
}
- private int getSubIdForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ /**
+ * Returns the subscription ID for the given phone account handle.
+ *
+ * @param phoneAccountHandle the phone account handle for outgoing calls
+ * @return subscription ID for the given phone account handle; or
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+ * if not available; or throw a SecurityException if the caller doesn't have the
+ * permission.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getSubIdForPhoneAccountHandle(@NonNull PhoneAccountHandle phoneAccountHandle) {
int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
try {
- ITelecomService service = getTelecomService();
+ ITelephony service = getITelephony();
if (service != null) {
- retval = getSubIdForPhoneAccount(service.getPhoneAccount(phoneAccountHandle));
+ retval = service.getSubIdForPhoneAccountHandle(
+ phoneAccountHandle, mContext.getOpPackageName());
}
- } catch (RemoteException e) {
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getSubIdForPhoneAccountHandle RemoteException", ex);
+ ex.rethrowAsRuntimeException();
}
-
return retval;
}
@@ -10826,6 +10838,16 @@
}
/**
+ * Broadcast intent action for Ota emergency number database installation complete.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public static final String ACTION_OTA_EMERGENCY_NUMBER_DB_INSTALLED =
+ "android.telephony.action.OTA_EMERGENCY_NUMBER_DB_INSTALLED";
+
+ /**
* Returns whether {@link TelephonyManager#ACTION_EMERGENCY_ASSISTANCE emergency assistance} is
* available on the device.
* <p>
@@ -11466,10 +11488,13 @@
* 3) APN type is whitelisted. E.g. MMS is whitelisted if
* {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
*
+ * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
* @return whether data is enabled for a apn type.
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isDataEnabledForApn(@ApnSetting.ApnType int apnType) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
try {
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index ed292be..7cafa1e 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,7 +25,6 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -34,11 +33,9 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
-import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IIntegerConsumer;
@@ -46,8 +43,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -63,7 +58,7 @@
* @hide
*/
@SystemApi
-public class ImsMmTelManager {
+public class ImsMmTelManager implements RegistrationManager {
/**
* @hide
@@ -96,94 +91,18 @@
* Callback class for receiving IMS network Registration callback events.
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
* @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead.
*/
- public static class RegistrationCallback {
-
- private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
-
- // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
- // and WWAN are more accurate constants.
- private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
- new HashMap<Integer, Integer>() {{
- // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
- // case, since it is defined.
- put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
- put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- }};
-
- private final RegistrationCallback mLocalCallback;
- private Executor mExecutor;
-
- RegistrationBinder(RegistrationCallback localCallback) {
- mLocalCallback = localCallback;
- }
-
- @Override
- public void onRegistered(int imsRadioTech) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
- mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
- }
-
- @Override
- public void onRegistering(int imsRadioTech) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
- mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
- }
-
- @Override
- public void onDeregistered(ImsReasonInfo info) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onUnregistered(info)));
- }
-
- @Override
- public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
- getAccessType(imsRadioTech), info)));
- }
-
- @Override
- public void onSubscriberAssociatedUriChanged(Uri[] uris) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() ->
- mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
- }
-
- private void setExecutor(Executor executor) {
- mExecutor = executor;
- }
-
- private static int getAccessType(int regType) {
- if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
- Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: "
- + regType);
- return -1;
- }
- return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
- }
- }
-
- private final RegistrationBinder mBinder = new RegistrationBinder(this);
+ // Do not add to this class, add to RegistrationManager.RegistrationCallback instead.
+ @Deprecated
+ public static class RegistrationCallback extends RegistrationManager.RegistrationCallback {
/**
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
* @param imsTransportType the radio access technology.
*/
+ @Override
public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
}
@@ -192,6 +111,7 @@
*
* @param imsTransportType the radio access technology.
*/
+ @Override
public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
}
@@ -200,6 +120,7 @@
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
+ @Override
public void onUnregistered(@Nullable ImsReasonInfo info) {
}
@@ -209,33 +130,11 @@
* @param imsTransportType The transport type that has failed to handover registration to.
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
+ @Override
public void onTechnologyChangeFailed(
@AccessNetworkConstants.TransportType int imsTransportType,
@Nullable ImsReasonInfo info) {
}
-
- /**
- * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
- * it changes. Per RFC3455, an associated URI is a URI that the service provider has
- * allocated to a user for their own usage. A user's phone number is typically one of the
- * associated URIs.
- * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
- * subscription.
- * @hide
- */
- public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) {
- }
-
- /**@hide*/
- public final IImsRegistrationCallback getBinder() {
- return mBinder;
- }
-
- /**@hide*/
- //Only exposed as public for compatibility with deprecated ImsManager APIs.
- public void setExecutor(Executor executor) {
- mBinder.setExecutor(executor);
- }
}
/**
@@ -355,7 +254,10 @@
* the {@link ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
+ * @deprecated Use {@link #registerImsRegistrationCallback(
+ * RegistrationManager.RegistrationCallback, Executor)} instead.
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull RegistrationCallback c) throws ImsException {
@@ -380,6 +282,28 @@
}
}
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ try {
+ getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
/**
* Removes an existing {@link RegistrationCallback}.
*
@@ -390,7 +314,10 @@
* @param c The {@link RegistrationCallback} to be removed.
* @see SubscriptionManager.OnSubscriptionsChangedListener
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+ * @deprecated Use {@link #unregisterImsRegistrationCallback(
+ * RegistrationManager.RegistrationCallback)}.
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
if (c == null) {
@@ -403,6 +330,69 @@
}
}
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ try {
+ getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null callback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ try {
+ getITelephony().getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> stateCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getRegistrationTransportType(
+ @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (transportTypeCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null callback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ try {
+ getITelephony().getImsMmTelRegistrationTransportType(mSubId,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> transportTypeCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
/**
* Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
* availability updates for the subscription specified in
@@ -411,7 +401,7 @@
*
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
* subscription changed events and call
- * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+ * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up.
*
* When the callback is registered, it will initiate the callback c to be called with the
* current capabilities.
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 3c343dd..25bd1ca 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -22,12 +22,14 @@
import android.annotation.RequiresPermission;
import android.content.Context;
import android.os.Binder;
+import android.telephony.AccessNetworkConstants;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Manager for interfacing with the framework RCS services, including the User Capability Exchange
@@ -36,7 +38,7 @@
* Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager.
* @hide
*/
-public class ImsRcsManager {
+public class ImsRcsManager implements RegistrationManager {
/**
* Receives RCS availability status updates from the ImsService.
@@ -136,6 +138,64 @@
mSubId = subId;
}
+ /**{@inheritDoc}*/
+ @Override
+ public void registerImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c,
+ @NonNull @CallbackExecutor Executor executor)
+ throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ throw new UnsupportedOperationException("registerImsRegistrationCallback is not"
+ + "supported.");
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void unregisterImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ throw new UnsupportedOperationException("unregisterImsRegistrationCallback is not"
+ + "supported.");
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null stateCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ throw new UnsupportedOperationException("getRegistrationState is not"
+ + "supported.");
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void getRegistrationTransportType(
+ @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (transportTypeCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null transportTypeCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ throw new UnsupportedOperationException("getRegistrationTransportType is not"
+ + "supported.");
+ }
+
+
/**
* Registers an {@link AvailabilityCallback} with the system, which will provide RCS
* availability updates for the subscription specified.
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
new file mode 100644
index 0000000..b4c11e3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2019 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.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Binder;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manages IMS Service registration state for associated {@link ImsFeature}s.
+ * @hide
+ */
+@SystemApi
+public interface RegistrationManager {
+
+ /**
+ * @hide
+ */
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(prefix = "REGISTRATION_STATE_",
+ value = {
+ REGISTRATION_STATE_NOT_REGISTERED,
+ REGISTRATION_STATE_REGISTERING,
+ REGISTRATION_STATE_REGISTERED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsRegistrationState {}
+
+ /**
+ * The IMS service is currently not registered to the carrier network.
+ */
+ int REGISTRATION_STATE_NOT_REGISTERED = 0;
+
+ /**
+ * The IMS service is currently in the process of registering to the carrier network.
+ */
+ int REGISTRATION_STATE_REGISTERING = 1;
+
+ /**
+ * The IMS service is currently registered to the carrier network.
+ */
+ int REGISTRATION_STATE_REGISTERED = 2;
+
+
+ /**@hide*/
+ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+ // and WWAN are more accurate constants.
+ Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
+ new HashMap<Integer, Integer>() {{
+ // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
+ // case, since it is defined.
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }};
+
+ /**
+ * Callback class for receiving IMS network Registration callback events.
+ * @see #registerImsRegistrationCallback(RegistrationCallback, Executor)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ */
+ class RegistrationCallback {
+
+ private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+ private final RegistrationCallback mLocalCallback;
+ private Executor mExecutor;
+
+ RegistrationBinder(RegistrationCallback localCallback) {
+ mLocalCallback = localCallback;
+ }
+
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
+ }
+
+ @Override
+ public void onRegistering(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
+ }
+
+ @Override
+ public void onDeregistered(ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onUnregistered(info)));
+ }
+
+ @Override
+ public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+ getAccessType(imsRadioTech), info)));
+ }
+
+ @Override
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+
+ private static int getAccessType(int regType) {
+ if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
+ Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: "
+ + regType);
+ return -1;
+ }
+ return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
+ }
+ }
+
+ private final RegistrationBinder mBinder = new RegistrationBinder(this);
+
+ /**
+ * Notifies the framework when the IMS Provider is registered to the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is trying to register the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ public void onUnregistered(@Nullable ImsReasonInfo info) {
+ }
+
+ /**
+ * A failure has occurred when trying to handover registration to another technology type.
+ *
+ * @param imsTransportType The transport type that has failed to handover registration to.
+ * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+ */
+ public void onTechnologyChangeFailed(
+ @AccessNetworkConstants.TransportType int imsTransportType,
+ @Nullable ImsReasonInfo info) {
+ }
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+ * allocated to a user for their own usage. A user's phone number is typically one of the
+ * associated URIs.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ * @hide
+ */
+ public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) {
+ }
+
+ /**@hide*/
+ public final IImsRegistrationCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ //Only exposed as public for compatibility with deprecated ImsManager APIs.
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ /**
+ * Registers a {@link RegistrationCallback} with the system. Use
+ * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+ * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current registration state.
+ *
+ * @param c The {@link RegistrationCallback} to be added.
+ * @param executor The executor the callback events should be run on.
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ * @throws ImsException if the subscription associated with this callback is valid, but
+ * the {@link ImsService} associated with the subscription is not available. This can happen if
+ * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+ * reason.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void registerImsRegistrationCallback(@NonNull RegistrationCallback c,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException;
+
+ /**
+ * Removes an existing {@link RegistrationCallback}.
+ *
+ * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+ * etc...), this callback will automatically be removed. If this method is called for an
+ * inactive subscription, it will result in a no-op.
+ *
+ * @param c The {@link RegistrationCallback} to be removed.
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #registerImsRegistrationCallback(RegistrationCallback, Executor)
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c);
+
+ /**
+ * Gets the registration state of the IMS service.
+ * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+ * registration state of the IMS service, which will be one of the
+ * following: {@link #REGISTRATION_STATE_NOT_REGISTERED},
+ * {@link #REGISTRATION_STATE_REGISTERING}, or
+ * {@link #REGISTRATION_STATE_REGISTERED}.
+ * @param executor The {@link Executor} that will be used to call the IMS registration state
+ * callback.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback,
+ @NonNull @CallbackExecutor Executor executor);
+
+ /**
+ * Gets the Transport Type associated with the current IMS registration.
+ * @param transportTypeCallback The transport type associated with the current IMS registration,
+ * which will be one of following:
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
+ * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void getRegistrationTransportType(
+ @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback,
+ @NonNull @CallbackExecutor Executor executor);
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index a08e031..b455c2e 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -22,6 +22,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
@@ -72,9 +73,6 @@
// with NOT_REGISTERED in the case where the ImsService has not updated the registration state
// yet.
private static final int REGISTRATION_STATE_UNKNOWN = -1;
- private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
- private static final int REGISTRATION_STATE_REGISTERING = 1;
- private static final int REGISTRATION_STATE_REGISTERED = 2;
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@@ -128,7 +126,7 @@
* {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
*/
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
+ updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
mCallbacks.broadcast((c) -> {
try {
c.onRegistered(imsRadioTech);
@@ -146,7 +144,7 @@
* {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
*/
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
+ updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
mCallbacks.broadcast((c) -> {
try {
c.onRegistering(imsRadioTech);
@@ -230,7 +228,8 @@
private void updateToDisconnectedState(ImsReasonInfo info) {
synchronized (mLock) {
- updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED);
+ updateToState(REGISTRATION_TECH_NONE,
+ RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
if (info != null) {
mLastDisconnectCause = info;
} else {
@@ -264,15 +263,15 @@
disconnectInfo = mLastDisconnectCause;
}
switch (state) {
- case REGISTRATION_STATE_NOT_REGISTERED: {
+ case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: {
c.onDeregistered(disconnectInfo);
break;
}
- case REGISTRATION_STATE_REGISTERING: {
+ case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
c.onRegistering(getConnectionType());
break;
}
- case REGISTRATION_STATE_REGISTERED: {
+ case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
c.onRegistered(getConnectionType());
break;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ab3861e..414693e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1309,6 +1309,12 @@
int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
/**
+ * Returns the subscription ID associated with the specified PhoneAccountHandle.
+ */
+ int getSubIdForPhoneAccountHandle(in PhoneAccountHandle phoneAccountHandle,
+ String callingPackage);
+
+ /**
* Returns the PhoneAccountHandle associated with a subscription ID.
*/
PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId);
@@ -1796,6 +1802,16 @@
void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
/**
+ * Get the IMS service registration state for the MmTelFeature associated with this sub id.
+ */
+ void getImsMmTelRegistrationState(int subId, IIntegerConsumer consumer);
+
+ /**
+ * Get the transport type for the IMS service registration state.
+ */
+ void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer);
+
+ /**
* Adds an IMS MmTel capabilities callback for the subscription specified.
*/
void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index f17a1a5..5dcc751 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -40,7 +40,6 @@
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
-import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -1037,9 +1036,6 @@
* Caller must pass in the correct user context if calling from a singleton service.
*/
public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
- if (SmsManager.getDefault().getAutoPersisting()) {
- return true;
- }
return !isDefaultSmsApplication(context, packageName);
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 81b1e49..0b5d446 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -19,8 +19,14 @@
java_sdk_library {
name: "android.test.mock",
- srcs: ["src/**/*.java"],
- api_srcs: [":framework-all-sources"],
+ srcs: [
+ "src/**/*.java",
+ // Note: Below are NOT APIs of this library. We only take APIs under
+ // the android.test.mock package. They however provide private APIs that
+ // android.test.mock APIs references to.
+ ":framework-core-sources-for-test-mock",
+ ":framework_native_aidl",
+ ],
libs: ["framework-all"],
api_packages: [