am b732b7b5: am ce57a7f3: am 6504490c: am dff6b8e7: Merge "Add --non-constant-id to aapt."
* commit 'b732b7b5e8192501360edc15fb8c6399d11fb97d':
GpsLocationProvider: Clean up HAL initialization/cleanup sequence
Fixed GSM encoded network initiated position request
Ensuring thread-safe usage of DateFormat.
Fixing infinite loop for zero duration.
Fix for an infinite loop while scrolling lists.
WAPPushManager, WAP Push over SMS message handler
Add --non-constant-id to aapt.
diff --git a/Android.mk b/Android.mk
index 08ee65e..6e41f96 100644
--- a/Android.mk
+++ b/Android.mk
@@ -188,6 +188,7 @@
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IIccPhoneBook.aidl \
telephony/java/com/android/internal/telephony/ISms.aidl \
+ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl \
vpn/java/android/net/vpn/IVpnService.aidl \
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index a95dad7..353b628 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -642,14 +642,18 @@
private static void initFormatStrings() {
synchronized (sLock) {
- Resources r = Resources.getSystem();
- Configuration cfg = r.getConfiguration();
- if (sLastConfig == null || !sLastConfig.equals(cfg)) {
- sLastConfig = cfg;
- sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
- sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
- sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
- }
+ initFormatStringsLocked();
+ }
+ }
+
+ private static void initFormatStringsLocked() {
+ Resources r = Resources.getSystem();
+ Configuration cfg = r.getConfiguration();
+ if (sLastConfig == null || !sLastConfig.equals(cfg)) {
+ sLastConfig = cfg;
+ sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
+ sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
+ sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
}
}
@@ -659,8 +663,10 @@
* @hide
*/
public static final CharSequence timeString(long millis) {
- initFormatStrings();
- return sStatusTimeFormat.format(millis);
+ synchronized (sLock) {
+ initFormatStringsLocked();
+ return sStatusTimeFormat.format(millis);
+ }
}
/**
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index d7b0dc1..9042505 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -191,7 +191,7 @@
int pos = 0;
fieldLen -= 1;
while (pos < fieldLen) {
- formatStr[pos] = ' ';
+ formatStr[pos++] = ' ';
}
formatStr[pos] = '0';
return pos+1;
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 2a0e849..c2cb0a0 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -339,6 +339,7 @@
mEdgeScaleY = mEdgeScaleYStart +
(mEdgeScaleYFinish - mEdgeScaleYStart) *
interp * factor;
+ mState = STATE_RECEDE;
break;
case STATE_RECEDE:
mState = STATE_IDLE;
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index d539833..ffc3346 100755
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -29,6 +29,7 @@
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.telephony.GsmAlphabet;
/**
* A GPS Network-initiated Handler class used by LocationManager.
@@ -288,58 +289,32 @@
*/
static String decodeGSMPackedString(byte[] input)
{
- final char CHAR_CR = 0x0D;
- int nStridx = 0;
- int nPckidx = 0;
- int num_bytes = input.length;
- int cPrev = 0;
- int cCurr = 0;
- byte nShift;
- byte nextChar;
- byte[] stringBuf = new byte[input.length * 2];
- String result = "";
+ final char PADDING_CHAR = 0x00;
+ int lengthBytes = input.length;
+ int lengthSeptets = (lengthBytes * 8) / 7;
+ String decoded;
- while(nPckidx < num_bytes)
- {
- nShift = (byte) (nStridx & 0x07);
- cCurr = input[nPckidx++];
- if (cCurr < 0) cCurr += 256;
-
- /* A 7-bit character can be split at the most between two bytes of packed
- ** data.
- */
- nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
- stringBuf[nStridx++] = nextChar;
-
- /* Special case where the whole of the next 7-bit character fits inside
- ** the current byte of packed data.
- */
- if(nShift == 6)
- {
- /* If the next 7-bit character is a CR (0x0D) and it is the last
- ** character, then it indicates a padding character. Drop it.
- */
- if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
- {
- break;
+ /* Special case where the last 7 bits in the last byte could hold a valid
+ * 7-bit character or a padding character. Drop the last 7-bit character
+ * if it is a padding character.
+ */
+ if (lengthBytes % 7 == 0) {
+ if (lengthBytes > 0) {
+ if ((input[lengthBytes - 1] >> 1) == PADDING_CHAR) {
+ lengthSeptets = lengthSeptets - 1;
}
-
- nextChar = (byte) (cCurr >> 1);
- stringBuf[nStridx++] = nextChar;
}
-
- cPrev = cCurr;
}
- try {
- result = new String(stringBuf, 0, nStridx, "US-ASCII");
- }
- catch (UnsupportedEncodingException e)
- {
- Log.e(TAG, e.getMessage());
+ decoded = GsmAlphabet.gsm7BitPackedToString(input, 0, lengthSeptets);
+
+ // Return "" if decoding of GSM packed string fails
+ if (null == decoded) {
+ Log.e(TAG, "Decoding of GSM packed string failed");
+ decoded = "";
}
- return result;
+ return decoded;
}
static String decodeUTF8String(byte[] input)
diff --git a/packages/WAPPushManager/Android.mk b/packages/WAPPushManager/Android.mk
new file mode 100644
index 0000000..c1d8f4b
--- /dev/null
+++ b/packages/WAPPushManager/Android.mk
@@ -0,0 +1,20 @@
+# Copyright 2007-2008 The Android Open Source Project
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WAPPushManager
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
+LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+
+include $(BUILD_PACKAGE)
+
+# This finds and builds the test apk as well, so a single make does both.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
new file mode 100644
index 0000000..89e9d6a
--- /dev/null
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.smspush">
+
+ <permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND"
+ android:protectionLevel="signatureOrSystem" />
+
+ <original-package android:name="com.android.smspush" />
+ <application>
+ <service android:name=".WapPushManager"
+ android:permission="com.android.smspush.WAPPUSH_MANAGER_BIND"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.internal.telephony.IWapPushManager"></action>
+ </intent-filter>
+ </service>
+ </application>
+
+
+</manifest>
diff --git a/packages/WAPPushManager/CleanSpec.mk b/packages/WAPPushManager/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/packages/WAPPushManager/CleanSpec.mk
@@ -0,0 +1,49 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/WAPPushManager/MODULE_LICENSE_APACHE2 b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
diff --git a/packages/WAPPushManager/NOTICE b/packages/WAPPushManager/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/WAPPushManager/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/packages/WAPPushManager/proguard.flags b/packages/WAPPushManager/proguard.flags
new file mode 100644
index 0000000..d09887b
--- /dev/null
+++ b/packages/WAPPushManager/proguard.flags
@@ -0,0 +1,18 @@
+
+#apply method is dynamically referenced by the reflection class.
+-keep class android.app.ContextImpl$SharedPreferencesImpl$EditorImpl {
+ void apply();
+}
+-keep class android.content.SharedPreferences$Editor {
+ void apply();
+}
+
+#WapPushManager is referenced only by AndroidManifest.xml
+-keep class com.android.smspush.WapPushManager {
+#CTS module method
+ public boolean isDataExist(java.lang.String, java.lang.String,
+ java.lang.String, java.lang.String);
+ public boolean verifyData(java.lang.String, java.lang.String,
+ java.lang.String, java.lang.String, int, boolean, boolean);
+}
+
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
new file mode 100644
index 0000000..96e0377
--- /dev/null
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2010 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.smspush;
+
+import android.app.Service;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+
+/**
+ * The WapPushManager service is implemented to process incoming
+ * WAP Push messages and to maintain the Receiver Application/Application
+ * ID mapping. The WapPushManager runs as a system service, and only the
+ * WapPushManager can update the WAP Push message and Receiver Application
+ * mapping (Application ID Table). When the device receives an SMS WAP Push
+ * message, the WapPushManager looks up the Receiver Application name in
+ * Application ID Table. If an application is found, the application is
+ * launched using its full component name instead of broadcasting an implicit
+ * Intent. If a Receiver Application is not found in the Application ID
+ * Table or the WapPushManager returns a process-further value, the
+ * telephony stack will process the message using existing message processing
+ * flow, and broadcast an implicit Intent.
+ */
+public class WapPushManager extends Service {
+
+ private static final String LOG_TAG = "WAP PUSH";
+ private static final String DATABASE_NAME = "wappush.db";
+ private static final String APPID_TABLE_NAME = "appid_tbl";
+
+ /**
+ * Version number must be incremented when table structure is changed.
+ */
+ private static final int WAP_PUSH_MANAGER_VERSION = 1;
+ private static final boolean DEBUG_SQL = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * Inner class that deals with application ID table
+ */
+ private class WapPushManDBHelper extends SQLiteOpenHelper {
+ WapPushManDBHelper(Context context) {
+ super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION);
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created.");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "db onCreate.");
+ String sql = "CREATE TABLE " + APPID_TABLE_NAME + " ("
+ + "id INTEGER PRIMARY KEY, "
+ + "x_wap_application TEXT, "
+ + "content_type TEXT, "
+ + "package_name TEXT, "
+ + "class_name TEXT, "
+ + "app_type INTEGER, "
+ + "need_signature INTEGER, "
+ + "further_processing INTEGER, "
+ + "install_order INTEGER "
+ + ")";
+
+ if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+ db.execSQL(sql);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db,
+ int oldVersion, int newVersion) {
+ // TODO: when table structure is changed, need to dump and restore data.
+ /*
+ db.execSQL(
+ "drop table if exists "+APPID_TABLE_NAME);
+ onCreate(db);
+ */
+ Log.w(LOG_TAG, "onUpgrade is not implemented yet. do nothing.");
+ }
+
+ protected class queryData {
+ public String packageName;
+ public String className;
+ int appType;
+ int needSignature;
+ int furtherProcessing;
+ int installOrder;
+ }
+
+ /**
+ * Query the latest receiver application info with supplied application ID and
+ * content type.
+ * @param app_id application ID to look up
+ * @param content_type content type to look up
+ */
+ protected queryData queryLastApp(SQLiteDatabase db,
+ String app_id, String content_type) {
+ String sql = "select install_order, package_name, class_name, "
+ + " app_type, need_signature, further_processing"
+ + " from " + APPID_TABLE_NAME
+ + " where x_wap_application=\'" + app_id + "\'"
+ + " and content_type=\'" + content_type + "\'"
+ + " order by install_order desc";
+ if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+ Cursor cur = db.rawQuery(sql, null);
+ queryData ret = null;
+
+ if (cur.moveToNext()) {
+ ret = new queryData();
+ ret.installOrder = cur.getInt(cur.getColumnIndex("install_order"));
+ ret.packageName = cur.getString(cur.getColumnIndex("package_name"));
+ ret.className = cur.getString(cur.getColumnIndex("class_name"));
+ ret.appType = cur.getInt(cur.getColumnIndex("app_type"));
+ ret.needSignature = cur.getInt(cur.getColumnIndex("need_signature"));
+ ret.furtherProcessing = cur.getInt(cur.getColumnIndex("further_processing"));
+ }
+ cur.close();
+ return ret;
+ }
+
+ }
+
+ /**
+ * The exported API implementations class
+ */
+ private class IWapPushManagerStub extends IWapPushManager.Stub {
+ public Context mContext;
+
+ public IWapPushManagerStub() {
+
+ }
+
+ /**
+ * Compare the package signature with WapPushManager package
+ */
+ protected boolean signatureCheck(String package_name) {
+ PackageManager pm = mContext.getPackageManager();
+ int match = pm.checkSignatures(mContext.getPackageName(), package_name);
+
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "compare signature " + mContext.getPackageName()
+ + " and " + package_name + ", match=" + match);
+
+ return match == PackageManager.SIGNATURE_MATCH;
+ }
+
+ /**
+ * Returns the status value of the message processing.
+ * The message will be processed as follows:
+ * 1.Look up Application ID Table with x-wap-application-id + content type
+ * 2.Check the signature of package name that is found in the
+ * Application ID Table by using PackageManager.checkSignature
+ * 3.Trigger the Application
+ * 4.Returns the process status value.
+ */
+ public int processMessage(String app_id, String content_type, Intent intent)
+ throws RemoteException {
+ Log.d(LOG_TAG, "wpman processMsg " + app_id + ":" + content_type);
+
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getReadableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, app_id, content_type);
+ db.close();
+
+ if (lastapp == null) {
+ Log.w(LOG_TAG, "no receiver app found for " + app_id + ":" + content_type);
+ return WapPushManagerParams.APP_QUERY_FAILED;
+ }
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "starting " + lastapp.packageName
+ + "/" + lastapp.className);
+
+ if (lastapp.needSignature != 0) {
+ if (!signatureCheck(lastapp.packageName)) {
+ return WapPushManagerParams.SIGNATURE_NO_MATCH;
+ }
+ }
+
+ if (lastapp.appType == WapPushManagerParams.APP_TYPE_ACTIVITY) {
+ //Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(lastapp.packageName, lastapp.className);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ } else {
+ intent.setClassName(mContext, lastapp.className);
+ intent.setComponent(new ComponentName(lastapp.packageName,
+ lastapp.className));
+ if (mContext.startService(intent) == null) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ }
+
+ return WapPushManagerParams.MESSAGE_HANDLED
+ | (lastapp.furtherProcessing == 1 ?
+ WapPushManagerParams.FURTHER_PROCESSING : 0);
+ }
+
+ protected boolean appTypeCheck(int app_type) {
+ if (app_type == WapPushManagerParams.APP_TYPE_ACTIVITY ||
+ app_type == WapPushManagerParams.APP_TYPE_SERVICE) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if adding the package succeeded.
+ */
+ public boolean addPackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing) {
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getWritableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+ boolean ret = false;
+ boolean insert = false;
+ int sq = 0;
+
+ if (!appTypeCheck(app_type)) {
+ Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+ + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+ + WapPushManagerParams.APP_TYPE_SERVICE);
+ return false;
+ }
+
+ if (lastapp == null) {
+ insert = true;
+ sq = 0;
+ } else if (!lastapp.packageName.equals(package_name) ||
+ !lastapp.className.equals(class_name)) {
+ insert = true;
+ sq = lastapp.installOrder + 1;
+ }
+
+ if (insert) {
+ ContentValues values = new ContentValues();
+
+ values.put("x_wap_application", x_app_id);
+ values.put("content_type", content_type);
+ values.put("package_name", package_name);
+ values.put("class_name", class_name);
+ values.put("app_type", app_type);
+ values.put("need_signature", need_signature ? 1 : 0);
+ values.put("further_processing", further_processing ? 1 : 0);
+ values.put("install_order", sq);
+ db.insert(APPID_TABLE_NAME, null, values);
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type
+ + " " + package_name + "." + class_name
+ + ", newsq:" + sq);
+ ret = true;
+ }
+
+ db.close();
+
+ return ret;
+ }
+
+ /**
+ * Returns true if updating the package succeeded.
+ */
+ public boolean updatePackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing) {
+
+ if (!appTypeCheck(app_type)) {
+ Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+ + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+ + WapPushManagerParams.APP_TYPE_SERVICE);
+ return false;
+ }
+
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getWritableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+ if (lastapp == null) {
+ db.close();
+ return false;
+ }
+
+ ContentValues values = new ContentValues();
+ String where = "x_wap_application=\'" + x_app_id + "\'"
+ + " and content_type=\'" + content_type + "\'"
+ + " and install_order=" + lastapp.installOrder;
+
+ values.put("package_name", package_name);
+ values.put("class_name", class_name);
+ values.put("app_type", app_type);
+ values.put("need_signature", need_signature ? 1 : 0);
+ values.put("further_processing", further_processing ? 1 : 0);
+
+ int num = db.update(APPID_TABLE_NAME, values, where, null);
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "update:" + x_app_id + ":" + content_type + " "
+ + package_name + "." + class_name
+ + ", sq:" + lastapp.installOrder);
+
+ db.close();
+
+ return num > 0;
+ }
+
+ /**
+ * Returns true if deleting the package succeeded.
+ */
+ public boolean deletePackage(String x_app_id, String content_type,
+ String package_name, String class_name) {
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getWritableDatabase();
+ String where = "x_wap_application=\'" + x_app_id + "\'"
+ + " and content_type=\'" + content_type + "\'"
+ + " and package_name=\'" + package_name + "\'"
+ + " and class_name=\'" + class_name + "\'";
+ int num_removed = db.delete(APPID_TABLE_NAME, where, null);
+
+ db.close();
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "deleted " + num_removed + " rows:"
+ + x_app_id + ":" + content_type + " "
+ + package_name + "." + class_name);
+ return num_removed > 0;
+ }
+ };
+
+
+ /**
+ * Linux IPC Binder
+ */
+ private final IWapPushManagerStub mBinder = new IWapPushManagerStub();
+
+ /**
+ * Default constructor
+ */
+ public WapPushManager() {
+ super();
+ mBinder.mContext = this;
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return mBinder;
+ }
+
+ /**
+ * Application ID database instance
+ */
+ private WapPushManDBHelper mDbHelper = null;
+ protected WapPushManDBHelper getDatabase(Context context) {
+ if (mDbHelper == null) {
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst.");
+ mDbHelper = new WapPushManDBHelper(context);
+ }
+ return mDbHelper;
+ }
+
+
+ /**
+ * This method is used for testing
+ */
+ public boolean verifyData(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing) {
+ WapPushManDBHelper dbh = getDatabase(this);
+ SQLiteDatabase db = dbh.getReadableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+ db.close();
+
+ if (lastapp == null) return false;
+
+ if (lastapp.packageName.equals(package_name)
+ && lastapp.className.equals(class_name)
+ && lastapp.appType == app_type
+ && lastapp.needSignature == (need_signature ? 1 : 0)
+ && lastapp.furtherProcessing == (further_processing ? 1 : 0)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This method is used for testing
+ */
+ public boolean isDataExist(String x_app_id, String content_type,
+ String package_name, String class_name) {
+ WapPushManDBHelper dbh = getDatabase(this);
+ SQLiteDatabase db = dbh.getReadableDatabase();
+ boolean ret = dbh.queryLastApp(db, x_app_id, content_type) != null;
+
+ db.close();
+ return ret;
+ }
+
+}
+
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
new file mode 100644
index 0000000..0a95b52
--- /dev/null
+++ b/packages/WAPPushManager/tests/Android.mk
@@ -0,0 +1,38 @@
+# Copyright 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += \
+ src/com/android/smspush/unitTests/IDataVerify.aidl
+
+
+# Notice that we don't have to include the src files of Email because, by
+# running the tests using an instrumentation targeting Eamil, we
+# automatically get all of its classes loaded into our environment.
+
+LOCAL_PACKAGE_NAME := WAPPushManagerTests
+
+LOCAL_INSTRUMENTATION_FOR := WAPPushManager
+
+include $(BUILD_PACKAGE)
+
diff --git a/packages/WAPPushManager/tests/AndroidManifest.xml b/packages/WAPPushManager/tests/AndroidManifest.xml
new file mode 100644
index 0000000..da7634f
--- /dev/null
+++ b/packages/WAPPushManager/tests/AndroidManifest.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.smspush.unitTests">
+
+
+ <uses-permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND" />
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+ <!--testing.../-->
+ <application android:icon="@drawable/icon" android:label="wappush test">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name=".ClientTest"
+ android:label="wappush test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <receiver android:name=".DrmReceiver" android:enabled="true">
+ <intent-filter>
+ <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
+ <data android:mimeType="application/vnd.oma.drm.rights+xml" />
+ <data android:value="application/vnd.oma.drm.rights+wbxml" />
+ </intent-filter>
+ </receiver>
+
+ <service android:enabled="true" android:name=".ReceiverService"
+ android:exported="true"/>
+
+ <activity android:name=".ReceiverActivity"
+ android:exported="true" android:label="test receiver" />
+
+ <service android:name=".DataVerify"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.smspush.unitTests.IDataVerify" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+ <!--
+ This declares that this application uses the instrumentation test runner targeting
+ the package of com.android.smspush. To run the tests use the command:
+ "adb shell am instrument -w
+ com.android.smspush.unitTests/android.test.InstrumentationTestRunner"
+ -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.smspush"
+ android:label="Tests for WAPPushManager"/>
+
+</manifest>
+
diff --git a/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/layout/main.xml b/packages/WAPPushManager/tests/res/layout/main.xml
new file mode 100644
index 0000000..c7bdbb2
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/layout/main.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<AbsoluteLayout
+android:id="@+id/widget133"
+android:layout_width="fill_parent"
+android:layout_height="fill_parent"
+xmlns:android="http://schemas.android.com/apk/res/android"
+>
+<EditText
+android:id="@+id/app_id"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="10"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/cont"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="20"
+android:textSize="18sp"
+android:layout_x="47px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/pkg"
+android:layout_width="125px"
+android:layout_height="wrap_content"
+android:text="pkg"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="81px"
+>
+</EditText>
+<EditText
+android:id="@+id/cls"
+android:layout_width="173px"
+android:layout_height="wrap_content"
+android:text="cls"
+android:textSize="18sp"
+android:layout_x="147px"
+android:layout_y="81px"
+>
+</EditText>
+<Button
+android:id="@+id/addpkg"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="add/update package"
+android:layout_x="15px"
+android:layout_y="225px"
+>
+</Button>
+<TextView
+android:id="@+id/widget52"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="input app_id, cont_type, pkg, cls"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="0px"
+>
+</TextView>
+<Button
+android:id="@+id/procmsg"
+android:layout_width="109px"
+android:layout_height="wrap_content"
+android:text="process msg"
+android:layout_x="197px"
+android:layout_y="361px"
+>
+</Button>
+<RadioGroup
+android:id="@+id/widget137"
+android:layout_width="83px"
+android:layout_height="80px"
+android:orientation="vertical"
+android:layout_x="19px"
+android:layout_y="137px"
+>
+<RadioButton
+android:id="@+id/act"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="act"
+android:checked="true"
+>
+</RadioButton>
+<RadioButton
+android:id="@+id/svc"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="svc"
+>
+</RadioButton>
+</RadioGroup>
+<Button
+android:id="@+id/delpkg"
+android:layout_width="174px"
+android:layout_height="wrap_content"
+android:text="delete pkg"
+android:layout_x="14px"
+android:layout_y="283px"
+>
+</Button>
+<EditText
+android:id="@+id/pdu"
+android:layout_width="186px"
+android:layout_height="83px"
+android:text="0006080302030aaf02905c030d6a0085070373616d706c6540646f636f6d6f2e6e652e6a700005c3072009102012345601"
+android:textSize="18sp"
+android:layout_x="10px"
+android:layout_y="341px"
+>
+</EditText>
+<CheckBox
+android:id="@+id/ftr"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="ftr_proc"
+android:layout_x="143px"
+android:layout_y="181px"
+>
+</CheckBox>
+<CheckBox
+android:id="@+id/sig"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="nd_sig"
+android:checked="true"
+android:layout_x="142px"
+android:layout_y="140px"
+>
+</CheckBox>
+</AbsoluteLayout>
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
new file mode 100644
index 0000000..78fd174
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 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.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RadioButton;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WapPushOverSms;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+/**
+ * WapPushManager test application
+ */
+public class ClientTest extends Activity {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ Button addpbtn = (Button) findViewById(R.id.addpkg);
+ Button procbtn = (Button) findViewById(R.id.procmsg);
+ Button delbtn = (Button) findViewById(R.id.delpkg);
+
+ Log.v(LOG_TAG, "activity created!!");
+
+ addpbtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ EditText app_id = (EditText) findViewById(R.id.app_id);
+ EditText cont = (EditText) findViewById(R.id.cont);
+ EditText pkg = (EditText) findViewById(R.id.pkg);
+ EditText cls = (EditText) findViewById(R.id.cls);
+ RadioButton act = (RadioButton) findViewById(R.id.act);
+ CheckBox sig = (CheckBox) findViewById(R.id.sig);
+ CheckBox ftr = (CheckBox) findViewById(R.id.ftr);
+
+ try {
+ if (!mWapPushMan.addPackage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ pkg.getText().toString(),
+ cls.getText().toString(),
+ act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+ WapPushManagerParams.APP_TYPE_SERVICE,
+ sig.isChecked(), ftr.isChecked())) {
+
+ Log.w(LOG_TAG, "remote add pkg failed...");
+ mWapPushMan.updatePackage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ pkg.getText().toString(),
+ cls.getText().toString(),
+ act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+ WapPushManagerParams.APP_TYPE_SERVICE,
+ sig.isChecked(), ftr.isChecked());
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ });
+
+ delbtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ EditText app_id = (EditText) findViewById(R.id.app_id);
+ EditText cont = (EditText) findViewById(R.id.cont);
+ EditText pkg = (EditText) findViewById(R.id.pkg);
+ EditText cls = (EditText) findViewById(R.id.cls);
+ // CheckBox delall = (CheckBox) findViewById(R.id.delall);
+ // Log.d(LOG_TAG, "button clicked");
+
+ try {
+ mWapPushMan.deletePackage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ pkg.getText().toString(),
+ cls.getText().toString());
+ // delall.isChecked());
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ });
+
+ procbtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ EditText pdu = (EditText) findViewById(R.id.pdu);
+ EditText app_id = (EditText) findViewById(R.id.app_id);
+ EditText cont = (EditText) findViewById(R.id.cont);
+
+ // WapPushOverSms wap = new WapPushOverSms();
+ // wap.dispatchWapPdu(strToHex(pdu.getText().toString()));
+ try {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", 0);
+ intent.putExtra("pduType", 6);
+ intent.putExtra("header",
+ HexDump.hexStringToByteArray(pdu.getText().toString()));
+ intent.putExtra("data",
+ HexDump.hexStringToByteArray(pdu.getText().toString()));
+
+ mWapPushMan.processMessage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ intent);
+ //HexDump.hexStringToByteArray(pdu.getText().toString()), 0, 6, 5, 5);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ });
+ }
+
+ private IWapPushManager mWapPushMan;
+ private ServiceConnection conn = new ServiceConnection() {
+ public void onServiceDisconnected(ComponentName name) {
+ mWapPushMan = null;
+ Log.v(LOG_TAG, "service disconnected.");
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mWapPushMan = IWapPushManager.Stub.asInterface(service);
+ Log.v(LOG_TAG, "service connected.");
+ }
+ };
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Log.v(LOG_TAG, "onStart bind WAPPushManager service "
+ + IWapPushManager.class.getName());
+ this.bindService(new Intent(IWapPushManager.class.getName()), conn,
+ Context.BIND_AUTO_CREATE);
+ Log.v(LOG_TAG, "bind service done.");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ this.unbindService(conn);
+ }
+
+}
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
new file mode 100644
index 0000000..ef491fd
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 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.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * To verify that receiver application receives correct body data.
+ */
+public class DataVerify extends Service {
+ private static final String LOG_TAG = "WAP PUSH";
+ private static final int TIME_WAIT = 100;
+ private static final int WAIT_COUNT = 100;
+ private static byte[] mLastReceivedPdu = null;
+ private static boolean sDataSet = false;
+
+ private class IDataVerifyStub extends IDataVerify.Stub {
+ public Context mContext;
+
+ public IDataVerifyStub() {
+ }
+
+ boolean arrayCompare(byte[] arr1, byte[] arr2) {
+ int i;
+
+ if (arr1 == null || arr2 == null) {
+ if (arr1 == null) {
+ Log.w(LOG_TAG, "arr1 is null");
+ } else {
+ Log.w(LOG_TAG, "arr2 is null");
+ }
+ return false;
+ }
+
+ if (arr1.length != arr2.length) {
+ return false;
+ }
+
+ for (i = 0; i < arr1.length; i++) {
+ if (arr1[i] != arr2[i]) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compare pdu and received pdu
+ */
+ public synchronized boolean verifyData(byte[] pdu) {
+ int cnt = 0;
+
+ while (!sDataSet) {
+ // wait for the activity receive data.
+ try {
+ Thread.sleep(TIME_WAIT);
+ if (cnt++ > WAIT_COUNT) {
+ // don't wait more than 10 sec.
+ return false;
+ }
+ } catch (InterruptedException e) {}
+ }
+
+ Log.v(LOG_TAG, "verify pdu");
+ boolean ret = arrayCompare(pdu, mLastReceivedPdu);
+ return ret;
+ }
+
+ /**
+ * Clear the old data. This method must be called before starting the test
+ */
+ public void resetData() {
+ mLastReceivedPdu = null;
+ sDataSet = false;
+ }
+ }
+
+ private final IDataVerifyStub binder = new IDataVerifyStub();
+
+ /**
+ * Constructor
+ */
+ public DataVerify() {
+ }
+
+ /**
+ * Receiver application must call this method when it receives the wap push message
+ */
+ public static void SetLastReceivedPdu(byte[] pdu) {
+ mLastReceivedPdu = pdu;
+ sDataSet = true;
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return binder;
+ }
+
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
new file mode 100644
index 0000000..5f5f121
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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.smspush.unitTests;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * A sample wap push receiver application for existing framework
+ * This class is listening for "application/vnd.oma.drm.rights+xml" message
+ */
+public class DrmReceiver extends BroadcastReceiver {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(LOG_TAG, "DrmReceiver received.");
+
+ byte[] body;
+ byte[] header;
+
+ body = intent.getByteArrayExtra("data");
+ header = intent.getByteArrayExtra("header");
+
+ Log.d(LOG_TAG, "header:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(header));
+ Log.d(LOG_TAG, "body:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+ DataVerify.SetLastReceivedPdu(body);
+ }
+
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
new file mode 100644
index 0000000..f0670fa
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 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.smspush.unitTests;
+
+/**
+ * Interface to receiver application data verifyer class
+ */
+interface IDataVerify {
+ /**
+ * Verify data
+ */
+ boolean verifyData(in byte[] pdu);
+
+ /**
+ * Initialize data
+ */
+ void resetData();
+}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
new file mode 100644
index 0000000..07f55ea
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 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.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Activity type receiver application
+ */
+public class ReceiverActivity extends Activity {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(LOG_TAG, "activity created!!");
+
+ Intent in = getIntent();
+ byte[] body;
+ byte[] header;
+
+ body = in.getByteArrayExtra("data");
+ header = in.getByteArrayExtra("header");
+
+ Log.d(LOG_TAG, "header:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(header));
+ Log.d(LOG_TAG, "body:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+ DataVerify.SetLastReceivedPdu(body);
+
+ finish();
+
+ }
+}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
new file mode 100644
index 0000000..b024bf5
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Service type receiver application
+ */
+public class ReceiverService extends Service {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(LOG_TAG, "Receiver service created");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(LOG_TAG, "Receiver service started");
+
+ byte[] body;
+ byte[] header;
+ body = intent.getByteArrayExtra("data");
+ header = intent.getByteArrayExtra("header");
+
+ Log.d(LOG_TAG, "header:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(header));
+ Log.d(LOG_TAG, "body:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+ DataVerify.SetLastReceivedPdu(body);
+ return START_STICKY;
+ }
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
new file mode 100644
index 0000000..9b0a36b
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -0,0 +1,2513 @@
+/*
+ * Copyright (C) 2010 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.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Telephony.Sms.Intents;
+import android.test.ServiceTestCase;
+import android.util.Log;
+import android.util.Config;
+
+import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+import java.util.Random;
+
+/**
+ * This is a simple framework for a test of a Service. See {@link android.test.ServiceTestCase
+ * ServiceTestCase} for more information on how to write and extend service tests.
+ *
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class com.android.smspush.unitTests.WapPushTest \
+ * com.android.smspush.unitTests/android.test.InstrumentationTestRunner
+ */
+public class WapPushTest extends ServiceTestCase<WapPushManager> {
+ private static final String LOG_TAG = "WAP PUSH";
+ private static final boolean LOCAL_LOGV = false;
+ private static final int TIME_WAIT = 100;
+
+ protected int mAppIdValue = 0x8002;
+ protected String mAppIdName = "x-wap-application:*";
+ protected int mContentTypeValue = 0x030a;
+ protected String mContentTypeName = "application/vnd.wap.sic";
+
+ protected String mPackageName;
+ protected String mClassName;
+
+ protected byte[] mGsmHeader = {
+ (byte) 0x00, // sc address
+ (byte) 0x40, // TP-MTI
+ (byte) 0x04, // sender address length?
+ (byte) 0x81, (byte) 0x55, (byte) 0x45, // sender address?
+ (byte) 0x00, // data schema
+ (byte) 0x00, // proto ID
+ (byte) 0x01, (byte) 0x60, (byte) 0x12, (byte) 0x31,
+ (byte) 0x74, (byte) 0x34, (byte) 0x63 // time stamp
+ };
+
+ protected byte[] mUserDataHeader = {
+ (byte) 0x07, // UDH len
+ (byte) 0x06, // header len
+ (byte) 0x05, // port addressing type?
+ (byte) 0x00, // dummy
+ (byte) 0x0B, (byte) 0x84, // dest port
+ (byte) 0x23, (byte) 0xF0 // src port
+ };
+
+ protected byte[] mWspHeader;
+
+ protected byte[] mMessageBody = {
+ (byte) 0x00,
+ (byte) 0x01,
+ (byte) 0x02,
+ (byte) 0x03,
+ (byte) 0x04,
+ (byte) 0x05,
+ (byte) 0x06,
+ (byte) 0x07,
+ (byte) 0x08,
+ (byte) 0x09,
+ (byte) 0x0a,
+ (byte) 0x0b,
+ (byte) 0x0c,
+ (byte) 0x0d,
+ (byte) 0x0e,
+ (byte) 0x0f
+ };
+
+ protected int mWspHeaderStart;
+ protected int mWspHeaderLen;
+ protected int mWspContentTypeStart;
+
+ /**
+ * OMA application ID in binary form
+ * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+ */
+ final int[] OMA_APPLICATION_ID_VALUES = new int[] {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0A,
+ 0x8000,
+ 0x8001,
+ 0x8002,
+ 0x8003,
+ 0x8004,
+ 0x8005,
+ 0x8006,
+ 0x8007,
+ 0x8008,
+ 0x8009,
+ 0x800B,
+ 0x8010
+ };
+
+ /**
+ * OMA application ID in string form
+ * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+ */
+ final String[] OMA_APPLICATION_ID_NAMES = new String[] {
+ "x-wap-application:*",
+ "x-wap-application:push.sia",
+ "x-wap-application:wml.ua",
+ "x-wap-application:wta.ua",
+ "x-wap-application:mms.ua",
+ "x-wap-application:push.syncml",
+ "x-wap-application:loc.ua",
+ "x-wap-application:syncml.dm",
+ "x-wap-application:drm.ua",
+ "x-wap-application:emn.ua",
+ "x-wap-application:wv.ua",
+ "x-wap-microsoft:localcontent.ua",
+ "x-wap-microsoft:IMclient.ua",
+ "x-wap-docomo:imode.mail.ua",
+ "x-wap-docomo:imode.mr.ua",
+ "x-wap-docomo:imode.mf.ua",
+ "x-motorola:location.ua",
+ "x-motorola:now.ua",
+ "x-motorola:otaprov.ua",
+ "x-motorola:browser.ua",
+ "x-motorola:splash.ua",
+ "x-wap-nai:mvsw.command",
+ "x-wap-openwave:iota.ua"
+ };
+
+ /**
+ * OMA content type in binary form
+ * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+ */
+ final int[] OMA_CONTENT_TYPE_VALUES = new int[] {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0A,
+ 0x0B,
+ 0x0C,
+ 0x0D,
+ 0x0E,
+ 0x0F,
+ 0x10,
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x19,
+ 0x1A,
+ 0x1B,
+ 0x1C,
+ 0x1D,
+ 0x1E,
+ 0x1F,
+ 0x20,
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28,
+ 0x29,
+ 0x2A,
+ 0x2B,
+ 0x2C,
+ 0x2D,
+ 0x2E,
+ 0x2F,
+ 0x30,
+ 0x31,
+ 0x32,
+ 0x33,
+ 0x34,
+ 0x35,
+ 0x36,
+ 0x37,
+ 0x38,
+ 0x39,
+ 0x3A,
+ 0x3B,
+ 0x3C,
+ 0x3D,
+ 0x3E,
+ 0x3F,
+ 0x40,
+ 0x41,
+ 0x42,
+ 0x43,
+ 0x44,
+ 0x45,
+ 0x46,
+ 0x47,
+ 0x48,
+ 0x49,
+ 0x4A,
+ 0x4B,
+ 0x4C,
+ 0x4D,
+ 0x4E,
+ 0x4F,
+ 0x50,
+ 0x51,
+ 0x52,
+ 0x53,
+ 0x54,
+// 0x55,
+// 0x56,
+// 0x57,
+// 0x58,
+ 0x0201,
+ 0x0202,
+ 0x0203,
+ 0x0204,
+ 0x0205,
+ 0x0206,
+ 0x0207,
+ 0x0208,
+ 0x0209,
+ 0x020A,
+ 0x020B,
+ 0x020C,
+ 0x0300,
+ 0x0301,
+ 0x0302,
+ 0x0303,
+ 0x0304,
+ 0x0305,
+ 0x0306,
+ 0x0307,
+ 0x0308,
+ 0x0309,
+ 0x030A,
+ 0x030B,
+ 0x030C,
+ 0x030D,
+ 0x030E,
+ 0x030F,
+ 0x0310,
+ 0x0311,
+ 0x0312,
+ 0x0313,
+ 0x0314,
+ 0x0315,
+ 0x0316,
+ 0x0317,
+ 0x0318,
+ 0x0319,
+ 0x031A,
+ 0x031B
+ /*0x031C,
+ 0x031D*/
+ };
+
+ /**
+ * OMA content type in string form
+ * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+ */
+ final String[] OMA_CONTENT_TYPE_NAMES = new String[] {
+ "*/*",
+ "text/*",
+ "text/html",
+ "text/plain",
+ "text/x-hdml",
+ "text/x-ttml",
+ "text/x-vCalendar",
+ "text/x-vCard",
+ "text/vnd.wap.wml",
+ "text/vnd.wap.wmlscript",
+ "text/vnd.wap.wta-event",
+ "multipart/*",
+ "multipart/mixed",
+ "multipart/form-data",
+ "multipart/byterantes",
+ "multipart/alternative",
+ "application/*",
+ "application/java-vm",
+ "application/x-www-form-urlencoded",
+ "application/x-hdmlc",
+ "application/vnd.wap.wmlc",
+ "application/vnd.wap.wmlscriptc",
+ "application/vnd.wap.wta-eventc",
+ "application/vnd.wap.uaprof",
+ "application/vnd.wap.wtls-ca-certificate",
+ "application/vnd.wap.wtls-user-certificate",
+ "application/x-x509-ca-cert",
+ "application/x-x509-user-cert",
+ "image/*",
+ "image/gif",
+ "image/jpeg",
+ "image/tiff",
+ "image/png",
+ "image/vnd.wap.wbmp",
+ "application/vnd.wap.multipart.*",
+ "application/vnd.wap.multipart.mixed",
+ "application/vnd.wap.multipart.form-data",
+ "application/vnd.wap.multipart.byteranges",
+ "application/vnd.wap.multipart.alternative",
+ "application/xml",
+ "text/xml",
+ "application/vnd.wap.wbxml",
+ "application/x-x968-cross-cert",
+ "application/x-x968-ca-cert",
+ "application/x-x968-user-cert",
+ "text/vnd.wap.si",
+ "application/vnd.wap.sic",
+ "text/vnd.wap.sl",
+ "application/vnd.wap.slc",
+ "text/vnd.wap.co",
+ "application/vnd.wap.coc",
+ "application/vnd.wap.multipart.related",
+ "application/vnd.wap.sia",
+ "text/vnd.wap.connectivity-xml",
+ "application/vnd.wap.connectivity-wbxml",
+ "application/pkcs7-mime",
+ "application/vnd.wap.hashed-certificate",
+ "application/vnd.wap.signed-certificate",
+ "application/vnd.wap.cert-response",
+ "application/xhtml+xml",
+ "application/wml+xml",
+ "text/css",
+ "application/vnd.wap.mms-message",
+ "application/vnd.wap.rollover-certificate",
+ "application/vnd.wap.locc+wbxml",
+ "application/vnd.wap.loc+xml",
+ "application/vnd.syncml.dm+wbxml",
+ "application/vnd.syncml.dm+xml",
+ "application/vnd.syncml.notification",
+ "application/vnd.wap.xhtml+xml",
+ "application/vnd.wv.csp.cir",
+ "application/vnd.oma.dd+xml",
+ "application/vnd.oma.drm.message",
+ "application/vnd.oma.drm.content",
+ "application/vnd.oma.drm.rights+xml",
+ "application/vnd.oma.drm.rights+wbxml",
+ "application/vnd.wv.csp+xml",
+ "application/vnd.wv.csp+wbxml",
+ "application/vnd.syncml.ds.notification",
+ "audio/*",
+ "video/*",
+ "application/vnd.oma.dd2+xml",
+ "application/mikey",
+ "application/vnd.oma.dcd",
+ "application/vnd.oma.dcdc",
+// "text/x-vMessage",
+// "application/vnd.omads-email+wbxml",
+// "text/x-vBookmark",
+// "application/vnd.syncml.dm.notification",
+ "application/vnd.uplanet.cacheop-wbxml",
+ "application/vnd.uplanet.signal",
+ "application/vnd.uplanet.alert-wbxml",
+ "application/vnd.uplanet.list-wbxml",
+ "application/vnd.uplanet.listcmd-wbxml",
+ "application/vnd.uplanet.channel-wbxml",
+ "application/vnd.uplanet.provisioning-status-uri",
+ "x-wap.multipart/vnd.uplanet.header-set",
+ "application/vnd.uplanet.bearer-choice-wbxml",
+ "application/vnd.phonecom.mmc-wbxml",
+ "application/vnd.nokia.syncset+wbxml",
+ "image/x-up-wpng",
+ "application/iota.mmc-wbxml",
+ "application/iota.mmc-xml",
+ "application/vnd.syncml+xml",
+ "application/vnd.syncml+wbxml",
+ "text/vnd.wap.emn+xml",
+ "text/calendar",
+ "application/vnd.omads-email+xml",
+ "application/vnd.omads-file+xml",
+ "application/vnd.omads-folder+xml",
+ "text/directory;profile=vCard",
+ "application/vnd.wap.emn+wbxml",
+ "application/vnd.nokia.ipdc-purchase-response",
+ "application/vnd.motorola.screen3+xml",
+ "application/vnd.motorola.screen3+gzip",
+ "application/vnd.cmcc.setting+wbxml",
+ "application/vnd.cmcc.bombing+wbxml",
+ "application/vnd.docomo.pf",
+ "application/vnd.docomo.ub",
+ "application/vnd.omaloc-supl-init",
+ "application/vnd.oma.group-usage-list+xml",
+ "application/oma-directory+xml",
+ "application/vnd.docomo.pf2",
+ "application/vnd.oma.drm.roap-trigger+wbxml",
+ "application/vnd.sbm.mid2",
+ "application/vnd.wmf.bootstrap",
+ "application/vnc.cmcc.dcd+xml",
+ "application/vnd.sbm.cid",
+ "application/vnd.oma.bcast.provisioningtrigger",
+ /*"application/vnd.docomo.dm",
+ "application/vnd.oma.scidm.messages+xml"*/
+ };
+
+ private IDataVerify mIVerify = null;
+
+ ServiceConnection mConn = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.v(LOG_TAG, "data verify interface connected.");
+ mIVerify = IDataVerify.Stub.asInterface(service);
+ }
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+
+ /**
+ * Main WapPushManager test module constructor
+ */
+ public WapPushTest() {
+ super(WapPushManager.class);
+ mClassName = this.getClass().getName();
+ mPackageName = this.getClass().getPackage().getName();
+ }
+
+ /**
+ * Initialize the verifier
+ */
+ @Override
+ public void setUp() {
+ try {
+ super.setUp();
+ // get verifier
+ getContext().bindService(new Intent(IDataVerify.class.getName()),
+ mConn, Context.BIND_AUTO_CREATE);
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "super exception");
+ }
+ // Log.d(LOG_TAG, "test setup");
+ }
+
+ private IWapPushManager mWapPush = null;
+ IWapPushManager getInterface() {
+ if (mWapPush != null) return mWapPush;
+ Intent startIntent = new Intent();
+ startIntent.setClass(getContext(), WapPushManager.class);
+ IBinder service = bindService(startIntent);
+
+ mWapPush = IWapPushManager.Stub.asInterface(service);
+ return mWapPush;
+ }
+
+ /*
+ * All methods need to start with 'test'.
+ * Use various assert methods to pass/fail the test case.
+ */
+ protected void utAddPackage(boolean need_sig, boolean more_proc) {
+ IWapPushManager iwapman = getInterface();
+
+ // insert new data
+ try {
+ assertTrue(iwapman.addPackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // verify the data
+ WapPushManager wpman = getService();
+ assertTrue(wpman.verifyData(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ }
+
+ /**
+ * Add package test
+ */
+ public void testAddPackage1() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ utAddPackage(true, true);
+ mAppIdValue += 10;
+ utAddPackage(true, false);
+ mContentTypeValue += 20;
+ utAddPackage(false, true);
+ mContentTypeValue += 20;
+ utAddPackage(false, false);
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+
+ // clean up data
+ try {
+ IWapPushManager iwapman = getInterface();
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mAppIdValue += 10;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * Add duprecated package test.
+ */
+ public void testAddPackage2() {
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ // set up data
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false);
+ iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false);
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+ false, false);
+
+ assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false));
+ assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false));
+ assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+ false, false));
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10), mPackageName, mClassName);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+ protected void utUpdatePackage(boolean need_sig, boolean more_proc) {
+ IWapPushManager iwapman = getInterface();
+
+ // insert new data
+ try {
+ assertTrue(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // verify the data
+ WapPushManager wpman = getService();
+ assertTrue(wpman.verifyData(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ }
+
+ /**
+ * Updating package test
+ */
+ public void testUpdatePackage1() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ // set up data
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mAppIdValue += 10;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ utUpdatePackage(false, false);
+ mAppIdValue += 10;
+ utUpdatePackage(false, true);
+ mContentTypeValue += 20;
+ utUpdatePackage(true, false);
+ mContentTypeValue += 20;
+ utUpdatePackage(true, true);
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+
+ // clean up data
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mAppIdValue += 10;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * Updating invalid package test
+ */
+ public void testUpdatePackage2() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ try {
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ assertFalse(iwapman.updatePackage(
+ Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName, 0, false, false));
+ assertFalse(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10),
+ mPackageName, mClassName, 0, false, false));
+ assertTrue(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName + "dummy_data", mClassName, 0, false, false));
+ assertTrue(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName + "dummy_data", 0, false, false));
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName,
+ mClassName + "dummy_data");
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+ protected void utDeletePackage() {
+ IWapPushManager iwapman = getInterface();
+
+ try {
+ assertTrue(iwapman.deletePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // verify the data
+ WapPushManager wpman = getService();
+ assertTrue(!wpman.isDataExist(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName));
+ }
+
+ /**
+ * Deleting package test
+ */
+ public void testDeletePackage1() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ // set up data
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mAppIdValue += 10;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ utDeletePackage();
+ mAppIdValue += 10;
+ utDeletePackage();
+ mContentTypeValue += 20;
+ utDeletePackage();
+ mContentTypeValue += 20;
+ utDeletePackage();
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * Deleting invalid package test
+ */
+ public void testDeletePackage2() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ try {
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+
+ assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName));
+ assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+ assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+
+ protected int encodeUint32(int uint32Val, byte[] arr, int start) {
+ int bit = 1;
+ int topbit = 0;
+ int encodeLen;
+ int tmpVal;
+
+ assertTrue(uint32Val >= 0);
+ for (int i = 0; i < 31; i++) {
+ if ((bit & uint32Val) > 0) topbit = i;
+ bit = (bit << 1);
+ }
+ encodeLen = topbit/7 + 1;
+ if (arr == null) return encodeLen;
+
+ //Log.d(LOG_TAG, "uint32Val = " + Integer.toHexString(uint32Val) + ", topbit = "
+ // + topbit + ", encodeLen = " + encodeLen);
+
+ tmpVal = uint32Val;
+ for (int i = encodeLen - 1; i >= 0; i--) {
+ long val = 0;
+ if (i < encodeLen - 1) val = 0x80;
+ val |= tmpVal & 0x7f;
+ arr[start + i] = (byte) (val & 0xFF);
+ tmpVal = (tmpVal >> 7);
+ }
+ return encodeLen;
+ }
+
+ protected int encodeShortInt(int sintVal, byte[] arr, int start) {
+ int encodeLen = 0;
+
+ if (sintVal >= 0x80) return encodeLen;
+ encodeLen = 1;
+ arr[start] = (byte) (sintVal | 0x80);
+ return encodeLen;
+ }
+
+
+ /**
+ * Generate Random WSP header with integer application ID
+ */
+ protected void createRandomWspHeader(byte[] arr, Random rd, int headerStart,
+ boolean noAppId) {
+
+ boolean appIdAdded = false;
+
+ Log.d(LOG_TAG, "headerStart = " + headerStart + ", appId = " + mAppIdValue
+ + "(" + Integer.toHexString(mAppIdValue) + ")");
+ Log.d(LOG_TAG, "random arr length:" + arr.length);
+ String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+ while (!appIdAdded) {
+ int type;
+ int index = headerStart;
+ int len = arr.length;
+ int i;
+ boolean addAppid = false;
+ int tmpVal = 0;
+ int tmpVal2 = 0;
+
+ while (true) {
+ int add;
+
+ /*
+ * field name
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * (no uint for param value)
+ */
+ type = rd.nextInt(3);
+ switch (type) {
+ case 0: // header short integer
+ if (index > 100 && !appIdAdded) addAppid = true;
+ add = 1;
+ break;
+ case 1: // header long int
+ add = 1 + rd.nextInt(29);
+ break;
+ default: // header string
+ add = 2 + rd.nextInt(10);
+ break;
+ }
+ if (index + add >= len) break;
+
+ // fill header name
+ switch (type) {
+ case 0: // header short integer
+ if (!addAppid) {
+ do {
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ } while (arr[index] == (byte) 0xaf);
+ } else {
+ Log.d(LOG_TAG, "appId added.");
+ arr[index] = (byte) 0xaf;
+ // if noAppId case, appId fld must be decieved.
+ if (noAppId) arr[index]++;
+ }
+ break;
+ case 1: // header long int
+ arr[index] = (byte) (add - 1);
+ tmpVal2 = 0;
+ for (i = 1; i < add; i++) {
+ tmpVal = rd.nextInt(255);
+ tmpVal2 = (tmpVal2 << 8) | tmpVal;
+ arr[index + i] = (byte) tmpVal;
+ }
+ // don't set application id
+ if (tmpVal2 == 0x2f) arr[index + 1]++;
+ break;
+ default: // header string
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field name index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i< index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+
+
+ /*
+ * field value
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * 3: uint
+ */
+ if (addAppid) {
+ type = 1;
+ } else {
+ type = rd.nextInt(4);
+ }
+ switch (type) {
+ case 0: // header short integer
+ add = 1;
+ break;
+ case 1: // header long int
+ if (addAppid) {
+ int bit = 1;
+ int topBit = 0;
+
+ for (i = 0; i < 31; i++) {
+ if ((mAppIdValue & bit) > 0) topBit = i;
+ bit = (bit << 1);
+ }
+ add = 2 + topBit/8;
+ } else {
+ add = 1 + rd.nextInt(29);
+ }
+ break;
+ case 2: // header string
+ add = 2 + rd.nextInt(10);
+ break;
+ default: // uint32
+ add = 6;
+ }
+ if (index + add >= len) break;
+
+ // fill field value
+ switch (type) {
+ case 0: // header short int
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ break;
+ case 1: // header long int
+ if (addAppid) {
+ addAppid = false;
+ appIdAdded = true;
+
+ arr[index] = (byte) (add - 1);
+ tmpVal = mAppIdValue;
+ for (i = add; i > 1; i--) {
+ arr[index + i - 1] = (byte) (tmpVal & 0xff);
+ tmpVal = (tmpVal >> 8);
+ }
+ } else {
+ arr[index] = (byte) (add - 1);
+ for (i = 1; i < add; i++) {
+ arr[index + i] = (byte) rd.nextInt(255);
+ }
+ }
+ break;
+ case 2:// header string
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ default: // header uvarint
+ arr[index] = (byte) 31;
+ tmpVal = rd.nextInt(0x0FFFFFFF);
+ add = 1 + encodeUint32(tmpVal, null, index + 1);
+ encodeUint32(tmpVal, arr, index + 1);
+ break;
+
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field value index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i< index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+ }
+ if (noAppId) break;
+ }
+
+ Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+ }
+
+ /**
+ * Generate Random WSP header with string application ID
+ */
+ protected void createRandomWspHeaderStrAppId(byte[] arr, Random rd, int headerStart,
+ boolean randomStr) {
+
+ boolean appIdAdded = false;
+
+ Log.d(LOG_TAG, "random arr length:" + arr.length);
+ String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+ while (!appIdAdded) {
+ int type;
+ int index = headerStart;
+ int len = arr.length;
+ int i;
+ boolean addAppid = false;
+ int tmpVal = 0;
+ int tmpVal2 = 0;
+
+ while (true) {
+ int add;
+
+ /*
+ * field name
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * (no uint for param value)
+ */
+ type = rd.nextInt(3);
+ switch (type) {
+ case 0: // header short integer
+ if (index > 100 && !appIdAdded) addAppid = true;
+ add = 1;
+ break;
+ case 1: // header long int
+ add = 1 + rd.nextInt(29);
+ break;
+ default: // header string
+ add = 2 + rd.nextInt(10);
+ break;
+ }
+ if (index + add >= len) break;
+
+ // fill header name
+ switch (type) {
+ case 0: // header short integer
+ if (!addAppid) {
+ do {
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ } while (arr[index] == (byte) 0xaf);
+ } else {
+ Log.d(LOG_TAG, "appId added.");
+ arr[index] = (byte) 0xaf;
+ }
+ break;
+ case 1: // header long int
+ arr[index] = (byte) (add - 1);
+ tmpVal2 = 0;
+ for (i = 1; i < add; i++) {
+ tmpVal = rd.nextInt(255);
+ tmpVal2 = (tmpVal2 << 8) | tmpVal;
+ arr[index + i] = (byte) tmpVal;
+ }
+ // don't set application id
+ if (tmpVal2 == 0x2f) arr[index + 1]++;
+ break;
+ default: // header string
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field name index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i < index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+
+
+ /*
+ * field value
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * 3: uint
+ */
+ if (addAppid) {
+ type = 2;
+ } else {
+ type = rd.nextInt(4);
+ }
+ switch (type) {
+ case 0: // header short integer
+ add = 1;
+ break;
+ case 1: // header long int
+ add = 1 + rd.nextInt(29);
+ break;
+ case 2: // header string
+ if (addAppid) {
+ if (randomStr) {
+ add = 1 + rd.nextInt(10);
+ byte[] randStr= new byte[add];
+ for (i = 0; i < add; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ randStr[i] = (byte) tmpVal;
+ }
+ mAppIdName = new String(randStr);
+ }
+ add = mAppIdName.length() + 1;
+ } else {
+ add = 2 + rd.nextInt(10);
+ }
+ break;
+ default: // uint32
+ add = 6;
+ }
+ if (index + add >= len) break;
+
+ // fill field value
+ switch (type) {
+ case 0: // header short int
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ break;
+ case 1: // header long int
+ arr[index] = (byte) (add - 1);
+ for (i = 1; i < add; i++)
+ arr[index + i] = (byte) rd.nextInt(255);
+ break;
+ case 2:// header string
+ if (addAppid) {
+ addAppid = false;
+ appIdAdded = true;
+ for (i = 0; i < add - 1; i++) {
+ arr[index + i] = (byte) (mAppIdName.charAt(i));
+ }
+ Log.d(LOG_TAG, "mAppIdName added [" + mAppIdName + "]");
+ } else {
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ default: // header uvarint
+ arr[index] = (byte) 31;
+ tmpVal = rd.nextInt(0x0FFFFFFF);
+ add = 1 + encodeUint32(tmpVal, null, index + 1);
+ encodeUint32(tmpVal, arr, index + 1);
+ break;
+
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field value index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i < index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+ }
+ }
+
+ Log.d(LOG_TAG, "headerStart = " + headerStart + ", mAppIdName = " + mAppIdName);
+ Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+ }
+
+ protected byte[] createPDU(int testNum) {
+ byte[] array = null;
+ // byte[] wsp = null;
+
+ switch (testNum) {
+ // sample pdu
+ case 1:
+ byte[] array1 = {
+ (byte) 0x00, // TID
+ (byte) 0x06, // Type = wap push
+ (byte) 0x00, // Length to be set later.
+
+ // Content-Type
+ (byte) 0x03, (byte) 0x02,
+ (byte) ((mContentTypeValue >> 8) & 0xff),
+ (byte) (mContentTypeValue & 0xff),
+
+ // Application-id
+ (byte) 0xaf, (byte) 0x02,
+ (byte) ((mAppIdValue >> 8) & 0xff),
+ (byte) (mAppIdValue& 0xff)
+ };
+ array1[2] = (byte) (array1.length - 3);
+ mWspHeader = array1;
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + 7;
+ mWspHeaderLen = array1.length;
+ break;
+
+ // invalid wsp header
+ case 2:
+ byte[] array2 = {
+ (byte) 0x00, // invalid data
+ };
+ mWspHeader = array2;
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length;
+ mWspHeaderLen = array2.length;
+ break;
+
+ // random wsp header
+ case 3:
+ Random rd = new Random();
+ int arrSize = 150 + rd.nextInt(100);
+ byte[] array3 = new byte[arrSize];
+ int hdrEncodeLen;
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeader(array3, rd, hdrEncodeLen + 6, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/o appid
+ case 4:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeader(array3, rd, hdrEncodeLen + 6, true);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ random appid string
+ case 5:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, true);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ OMA appid string
+ case 6:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ OMA content type
+ case 7:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+ // encode content type
+ int contentLen = mContentTypeName.length();
+ int next = 2 + hdrEncodeLen;
+ mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+ // next += encodeUint32(contentLen, array3, next);
+ int i;
+ Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+ + ", contentLen = " + contentLen);
+
+ for (i = 0; i < contentLen; i++) {
+ array3[next + i] = (byte) mContentTypeName.charAt(i);
+ }
+ array3[next + i] = (byte) 0x0;
+
+ createRandomWspHeader(array3, rd, next + contentLen + 1, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+ + next + contentLen + 1;
+ mWspHeaderLen = array3.length;
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ OMA content type, OMA app ID
+ case 8:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+ // encode content type
+ contentLen = mContentTypeName.length();
+ next = 2 + hdrEncodeLen;
+ mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+ // next += encodeUint32(contentLen, array3, next);
+ Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+ + ", contentLen = " + contentLen);
+
+ for (i = 0; i < contentLen; i++) {
+ array3[next + i] = (byte) mContentTypeName.charAt(i);
+ }
+ array3[next + i] = (byte) 0x0;
+
+ createRandomWspHeaderStrAppId(array3, rd, next + contentLen + 1, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+ + next + contentLen + 1;
+ mWspHeaderLen = array3.length;
+
+ mWspHeader = array3;
+ break;
+
+ default:
+ return null;
+ }
+ array = new byte[mGsmHeader.length + mUserDataHeader.length + mWspHeader.length
+ + mMessageBody.length];
+ System.arraycopy(mGsmHeader, 0, array, 0, mGsmHeader.length);
+ System.arraycopy(mUserDataHeader, 0, array,
+ mGsmHeader.length, mUserDataHeader.length);
+ System.arraycopy(mWspHeader, 0, array,
+ mGsmHeader.length + mUserDataHeader.length, mWspHeader.length);
+ System.arraycopy(mMessageBody, 0, array,
+ mGsmHeader.length + mUserDataHeader.length + mWspHeader.length,
+ mMessageBody.length);
+ return array;
+
+ }
+
+ Intent createIntent(int pduType, int tranId) {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", tranId);
+ intent.putExtra("pduType", pduType);
+ intent.putExtra("header", mGsmHeader);
+ intent.putExtra("data", mMessageBody);
+ // intent.putExtra("contentTypeParameters", null);
+ return intent;
+ }
+
+ /**
+ * Message processing test, start activity
+ */
+ public void testProcessMsg1() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ assertTrue((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * Message processing test, start service
+ */
+ public void testProcessMsg2() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length - (mGsmHeader.length +
+ mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ assertTrue((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * Message processing test, no signature
+ */
+ public void testProcessMsg3() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mPackageName = "com.android.development";
+ mClassName = "com.android.development.Development";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, true, false);
+
+ assertFalse((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ IDataVerify getVerifyInterface() {
+ while (mIVerify == null) {
+ // wait for the activity receive data.
+ try {
+ Thread.sleep(TIME_WAIT);
+ } catch (InterruptedException e) {}
+ }
+ return mIVerify;
+ }
+
+
+ /**
+ * Message processing test, received body data verification test
+ */
+ public void testProcessMsg4() {
+ byte[] originalMessageBody = mMessageBody;
+ mMessageBody = new byte[] {
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ };
+
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+
+ dataverify.resetData();
+
+ // set up data
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId));
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ assertTrue(dataverify.verifyData(mMessageBody));
+
+ // set up data
+ dataverify.resetData();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+ mMessageBody = new byte[] {
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ };
+ pdu = createPDU(1);
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId));
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ // Log.d(LOG_TAG, HexDump.dumpHexString(mMessageBody));
+ assertTrue(dataverify.verifyData(mMessageBody));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Message processing test, send invalid sms data
+ */
+ public void testProcessMsg5() {
+ byte[] pdu = createPDU(2);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ assertTrue((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * Message processing test, no receiver application
+ */
+ public void testProcessMsg6() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.NoReceiver";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ assertFalse((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ // set up data
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ assertFalse((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * WspTypeDecoder test, normal pdu
+ */
+ public void testDecoder1() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 10; i++) {
+ mAppIdValue = rd.nextInt(0xFFFF);
+ byte[] pdu = createPDU(1);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+ + ", val: " + pduDecoder.getValue32());
+ assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+ }
+
+ mAppIdValue = originalAppIdValue;
+ }
+
+ /**
+ * WspTypeDecoder test, no header
+ */
+ public void testDecoder2() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ Random rd = new Random();
+
+ mAppIdValue = rd.nextInt(0xFFFF);
+ byte[] pdu = createPDU(2);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertFalse(res);
+
+ mAppIdValue = originalAppIdValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode appid test
+ */
+ public void testDecoder3() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 100; i++) {
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(3);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+ + ", val: " + pduDecoder.getValue32());
+ assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /*
+ public void testEnc() {
+ byte[] arr = new byte[20];
+ int index = 0;
+ index += encodeUint32(0x87a5, arr, index);
+ index += encodeUint32(0x1, arr, index);
+ index += encodeUint32(0x9b, arr, index);
+ index += encodeUint32(0x10, arr, index);
+ index += encodeUint32(0xe0887, arr, index);
+ index += encodeUint32(0x791a23d0, arr, index);
+
+ Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+ }
+ */
+
+ /**
+ * WspTypeDecoder test, no appid test
+ */
+ public void testDecoder4() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 100; i++) {
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(4);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertFalse(res);
+
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode string appid test
+ */
+ public void testDecoder5() {
+ boolean res;
+ String originalAppIdName = mAppIdName;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 10; i++) {
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(5);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+ + pduDecoder.getValueString() + "]");
+ assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+ }
+
+ mAppIdName = originalAppIdName;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode string appid test
+ */
+ public void testDecoder6() {
+ boolean res;
+ String originalAppIdName = mAppIdName;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+ mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(6);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+ + pduDecoder.getValueString() + "]");
+ assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+ }
+
+ mAppIdName = originalAppIdName;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode OMA content type
+ */
+ public void testDecoder7() {
+ boolean res;
+ String originalAppIdName = mAppIdName;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+ mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+ byte[] pdu = createPDU(7);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.decodeContentType(mWspContentTypeStart);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mContentTypeName: [" + mContentTypeName + "], val: ["
+ + pduDecoder.getValueString() + "]");
+ assertTrue(mContentTypeName.equals(pduDecoder.getValueString()));
+ }
+
+ mAppIdName = originalAppIdName;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+
+ /**
+ * Copied from WapPushOverSms.
+ * The code flow is not changed from the original.
+ */
+ public int dispatchWapPdu(byte[] pdu, IWapPushManager wapPushMan) {
+
+ if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+
+ int index = 0;
+ int transactionId = pdu[index++] & 0xFF;
+ int pduType = pdu[index++] & 0xFF;
+ int headerLength = 0;
+
+ if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
+ (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+ return Intents.RESULT_SMS_HANDLED;
+ }
+
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ /**
+ * Parse HeaderLen(unsigned integer).
+ * From wap-230-wsp-20010705-a section 8.1.2
+ * The maximum size of a uintvar is 32 bits.
+ * So it will be encoded in no more than 5 octets.
+ */
+ if (pduDecoder.decodeUintvarInteger(index) == false) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ }
+ headerLength = (int) pduDecoder.getValue32();
+ index += pduDecoder.getDecodedDataLength();
+
+ int headerStartIndex = index;
+
+ /**
+ * Parse Content-Type.
+ * From wap-230-wsp-20010705-a section 8.4.2.24
+ *
+ * Content-type-value = Constrained-media | Content-general-form
+ * Content-general-form = Value-length Media-type
+ * Media-type = (Well-known-media | Extension-Media) *(Parameter)
+ * Value-length = Short-length | (Length-quote Length)
+ * Short-length = <Any octet 0-30> (octet <= WAP_PDU_SHORT_LENGTH_MAX)
+ * Length-quote = <Octet 31> (WAP_PDU_LENGTH_QUOTE)
+ * Length = Uintvar-integer
+ */
+ if (pduDecoder.decodeContentType(index) == false) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ }
+
+ String mimeType = pduDecoder.getValueString();
+ long binaryContentType = pduDecoder.getValue32();
+ index += pduDecoder.getDecodedDataLength();
+
+ byte[] header = new byte[headerLength];
+ System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
+
+ byte[] intentData;
+
+ if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ intentData = pdu;
+ } else {
+ int dataIndex = headerStartIndex + headerLength;
+ intentData = new byte[pdu.length - dataIndex];
+ System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
+ }
+
+ /**
+ * Seek for application ID field in WSP header.
+ * If application ID is found, WapPushManager substitute the message
+ * processing. Since WapPushManager is optional module, if WapPushManager
+ * is not found, legacy message processing will be continued.
+ */
+ if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
+ index = (int) pduDecoder.getValue32();
+ pduDecoder.decodeXWapApplicationId(index);
+ String wapAppId = pduDecoder.getValueString();
+ if (wapAppId == null) {
+ wapAppId = Integer.toString((int) pduDecoder.getValue32());
+ }
+
+ String contentType = ((mimeType == null) ?
+ Long.toString(binaryContentType) : mimeType);
+ if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+
+ try {
+ boolean processFurther = true;
+ // IWapPushManager wapPushMan = mWapConn.getWapPushManager();
+ if (wapPushMan == null) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+ } else {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", transactionId);
+ intent.putExtra("pduType", pduType);
+ intent.putExtra("header", header);
+ intent.putExtra("data", intentData);
+ intent.putExtra("contentTypeParameters",
+ pduDecoder.getContentParameters());
+
+ int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
+ if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+ if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
+ && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
+ processFurther = false;
+ }
+ }
+ if (!processFurther) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
+ } catch (RemoteException e) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+
+ return Activity.RESULT_OK;
+ }
+
+ protected byte[] retrieveWspBody() {
+ byte[] array = new byte[mWspHeader.length + mMessageBody.length];
+
+ System.arraycopy(mWspHeader, 0, array, 0, mWspHeader.length);
+ System.arraycopy(mMessageBody, 0, array, mWspHeader.length, mMessageBody.length);
+ return array;
+ }
+
+ protected String getContentTypeName(int ctypeVal) {
+ int i;
+
+ for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+ if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) {
+ return OMA_CONTENT_TYPE_NAMES[i];
+ }
+ }
+ return null;
+ }
+
+ protected boolean isContentTypeMapped(int ctypeVal) {
+ int i;
+
+ for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+ if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Integration test 1, simple case
+ */
+ public void testIntegration1() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(1);
+ byte[] wappushPdu = retrieveWspBody();
+
+
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+ // Phone dummy = new DummyPhone(getContext());
+ // Phone gsm = PhoneFactory.getGsmPhone();
+ // GSMPhone gsm = new GSMPhone(getContext(), new SimulatedCommands(), null, true);
+ // WapPushOverSms dispatcher = new WapPushOverSms(dummy, null);
+
+ try {
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+
+ dataverify.resetData();
+
+ if (isContentTypeMapped(mContentTypeValue)) {
+ // content type is mapped
+ mContentTypeName = getContentTypeName(mContentTypeValue);
+ Log.d(LOG_TAG, "mContentTypeValue mapping "
+ + mContentTypeName + ":" + mContentTypeValue);
+ } else {
+ mContentTypeName = Integer.toString(mContentTypeValue);
+ }
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName);
+
+ assertTrue(dataverify.verifyData(mMessageBody));
+ } catch (RemoteException e) {
+ }
+
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 2, random mAppIdValue(int), all OMA content type
+ */
+ public void testIntegration2() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+ for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+ mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(7);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 3, iterate OmaApplication ID, random binary content type
+ */
+ public void testIntegration3() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+ mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+ mContentTypeValue = rd.nextInt(0x0FFF);
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(6);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ if (isContentTypeMapped(mContentTypeValue)) {
+ // content type is mapped to integer value
+ mContentTypeName = getContentTypeName(mContentTypeValue);
+ Log.d(LOG_TAG, "mContentTypeValue mapping "
+ + mContentTypeValue + ":" + mContentTypeName);
+ } else {
+ mContentTypeName = Integer.toString(mContentTypeValue);
+ }
+
+ iwapman.addPackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 4, iterate OmaApplication ID, Oma content type
+ */
+ public void testIntegration4() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length
+ + OMA_CONTENT_TYPE_NAMES.length; i++) {
+ mAppIdName = OMA_APPLICATION_ID_NAMES[rd.nextInt(OMA_APPLICATION_ID_NAMES.length)];
+ int contIndex = rd.nextInt(OMA_CONTENT_TYPE_NAMES.length);
+ mContentTypeName = OMA_CONTENT_TYPE_NAMES[contIndex];
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(8);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ iwapman.addPackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 5, iterate binary OmaApplication ID, Oma binary content type
+ */
+ public void testIntegration5() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ for (int i = 0; i < OMA_APPLICATION_ID_VALUES.length +
+ OMA_CONTENT_TYPE_VALUES.length; i++) {
+ mAppIdValue = OMA_APPLICATION_ID_VALUES[rd.nextInt(
+ OMA_APPLICATION_ID_VALUES.length)];
+ mContentTypeValue =
+ OMA_CONTENT_TYPE_VALUES[rd.nextInt(OMA_CONTENT_TYPE_VALUES.length)];
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(3);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ if (isContentTypeMapped(mContentTypeValue)) {
+ // content type is mapped to integer value
+ mContentTypeName = getContentTypeName(mContentTypeValue);
+ Log.d(LOG_TAG, "mContentTypeValue mapping "
+ + mContentTypeValue + ":" + mContentTypeName);
+ } else {
+ mContentTypeName = Integer.toString(mContentTypeValue);
+ }
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+}
diff --git a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
new file mode 100644
index 0000000..d5ecb94
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 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.Intent;
+
+interface IWapPushManager {
+ /**
+ * Processes WAP push message and triggers the receiver application registered
+ * in the application ID table.
+ */
+ int processMessage(String app_id, String content_type, in Intent intent);
+
+ /**
+ * Add receiver application into the application ID table.
+ * Returns true if inserting the information is successfull. Inserting the duplicated
+ * record in the application ID table is not allowed. Use update/delete method.
+ */
+ boolean addPackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing);
+
+ /**
+ * Updates receiver application that is last added.
+ * Returns true if updating the information is successfull.
+ */
+ boolean updatePackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing);
+
+ /**
+ * Delites receiver application information.
+ * Returns true if deleting is successfull.
+ */
+ boolean deletePackage(String x_app_id, String content_type,
+ String package_name, String class_name);
+}
+
diff --git a/telephony/java/com/android/internal/telephony/WapPushManagerParams.java b/telephony/java/com/android/internal/telephony/WapPushManagerParams.java
new file mode 100644
index 0000000..11e5ff9
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/WapPushManagerParams.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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;
+
+/**
+ * WapPushManager constant value definitions
+ */
+public class WapPushManagerParams {
+ /**
+ * Application type activity
+ */
+ public static final int APP_TYPE_ACTIVITY = 0;
+
+ /**
+ * Application type service
+ */
+ public static final int APP_TYPE_SERVICE = 1;
+
+ /**
+ * Process Message return value
+ * Message is handled
+ */
+ public static final int MESSAGE_HANDLED = 0x1;
+
+ /**
+ * Process Message return value
+ * Application ID or content type was not found in the application ID table
+ */
+ public static final int APP_QUERY_FAILED = 0x2;
+
+ /**
+ * Process Message return value
+ * Receiver application signature check failed
+ */
+ public static final int SIGNATURE_NO_MATCH = 0x4;
+
+ /**
+ * Process Message return value
+ * Receiver application was not found
+ */
+ public static final int INVALID_RECEIVER_NAME = 0x8;
+
+ /**
+ * Process Message return value
+ * Unknown exception
+ */
+ public static final int EXCEPTION_CAUGHT = 0x10;
+
+ /**
+ * Process Message return value
+ * Need further processing after WapPushManager message processing
+ */
+ public static final int FURTHER_PROCESSING = 0x8000;
+
+}
+
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index 2f5d3ec..7704667 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -14,15 +14,20 @@
* limitations under the License.
*/
+
package com.android.internal.telephony;
import android.app.Activity;
import android.content.Context;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.util.Config;
import android.util.Log;
+import android.os.IBinder;
+import android.os.RemoteException;
/**
* WAP push handler class.
@@ -42,11 +47,83 @@
*/
private final int WAKE_LOCK_TIMEOUT = 5000;
+ private final int BIND_RETRY_INTERVAL = 1000;
+ /**
+ * A handle to WapPushManager interface
+ */
+ private WapPushConnection mWapConn = null;
+ private class WapPushConnection implements ServiceConnection {
+ private IWapPushManager mWapPushMan;
+ private Context mOwner;
+
+ public WapPushConnection(Context ownerContext) {
+ mOwner = ownerContext;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mWapPushMan = IWapPushManager.Stub.asInterface(service);
+ if (Config.DEBUG) Log.v(LOG_TAG, "wappush manager connected to " +
+ mOwner.hashCode());
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mWapPushMan = null;
+ if (Config.DEBUG) Log.v(LOG_TAG, "wappush manager disconnected.");
+ // WapPushManager must be always attached.
+ rebindWapPushManager();
+ }
+
+ /**
+ * bind WapPushManager
+ */
+ public void bindWapPushManager() {
+ if (mWapPushMan != null) return;
+
+ final ServiceConnection wapPushConnection = this;
+
+ mOwner.bindService(new Intent(IWapPushManager.class.getName()),
+ wapPushConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * rebind WapPushManager
+ * This method is called when WapPushManager is disconnected unexpectedly.
+ */
+ private void rebindWapPushManager() {
+ if (mWapPushMan != null) return;
+
+ final ServiceConnection wapPushConnection = this;
+ new Thread() {
+ public void run() {
+ while (mWapPushMan == null) {
+ mOwner.bindService(new Intent(IWapPushManager.class.getName()),
+ wapPushConnection, Context.BIND_AUTO_CREATE);
+ try {
+ Thread.sleep(BIND_RETRY_INTERVAL);
+ } catch (InterruptedException e) {
+ if (Config.DEBUG) Log.v(LOG_TAG, "sleep interrupted.");
+ }
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Returns interface to WapPushManager
+ */
+ public IWapPushManager getWapPushManager() {
+ return mWapPushMan;
+ }
+ }
+
public WapPushOverSms(Phone phone, SMSDispatcher smsDispatcher) {
mSmsDispatcher = smsDispatcher;
mContext = phone.getContext();
+ mWapConn = new WapPushConnection(mContext);
+ mWapConn.bindWapPushManager();
}
+
/**
* Dispatches inbound messages that are in the WAP PDU format. See
* wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
@@ -106,16 +183,15 @@
}
String mimeType = pduDecoder.getValueString();
-
+ long binaryContentType = pduDecoder.getValue32();
index += pduDecoder.getDecodedDataLength();
byte[] header = new byte[headerLength];
System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
byte[] intentData;
- String permission;
- if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
intentData = pdu;
} else {
int dataIndex = headerStartIndex + headerLength;
@@ -123,6 +199,62 @@
System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
}
+ /**
+ * Seek for application ID field in WSP header.
+ * If application ID is found, WapPushManager substitute the message
+ * processing. Since WapPushManager is optional module, if WapPushManager
+ * is not found, legacy message processing will be continued.
+ */
+ if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
+ index = (int) pduDecoder.getValue32();
+ pduDecoder.decodeXWapApplicationId(index);
+ String wapAppId = pduDecoder.getValueString();
+ if (wapAppId == null) {
+ wapAppId = Integer.toString((int) pduDecoder.getValue32());
+ }
+
+ String contentType = ((mimeType == null) ?
+ Long.toString(binaryContentType) : mimeType);
+ if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+
+ try {
+ boolean processFurther = true;
+ IWapPushManager wapPushMan = mWapConn.getWapPushManager();
+
+ if (wapPushMan == null) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+ } else {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", transactionId);
+ intent.putExtra("pduType", pduType);
+ intent.putExtra("header", header);
+ intent.putExtra("data", intentData);
+ intent.putExtra("contentTypeParameters",
+ pduDecoder.getContentParameters());
+
+ int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
+ if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+ if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
+ && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
+ processFurther = false;
+ }
+ }
+ if (!processFurther) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
+ } catch (RemoteException e) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+
+ if (mimeType == null) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Header Content-Type error.");
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ }
+
+ String permission;
+
if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
permission = "android.permission.RECEIVE_MMS";
} else {
@@ -141,4 +273,4 @@
return Activity.RESULT_OK;
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
index 6bf6b13..c8dd718 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -37,6 +37,7 @@
private final static HashMap<Integer, String> WELL_KNOWN_PARAMETERS =
new HashMap<Integer, String>();
+ public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f;
private static final int Q_VALUE = 0x00;
static {
@@ -603,6 +604,70 @@
}
/**
+ * Seek for the "X-Wap-Application-Id" field for WSP pdu
+ *
+ * @param startIndex The starting position of seek pointer
+ * @param endIndex Valid seek area end point
+ *
+ * @return false when error(not a X-Wap-Application-Id) occur
+ * return value can be retrieved by getValue32()
+ */
+ public boolean seekXWapApplicationId(int startIndex, int endIndex) {
+ int index = startIndex;
+
+ try {
+ for (index = startIndex; index <= endIndex; ) {
+ /**
+ * 8.4.1.1 Field name
+ * Field name is integer or text.
+ */
+ if (decodeIntegerValue(index)) {
+ int fieldValue = (int) getValue32();
+
+ if (fieldValue == PARAMETER_ID_X_WAP_APPLICATION_ID) {
+ unsigned32bit = index + 1;
+ return true;
+ }
+ } else {
+ if (!decodeTextString(index)) return false;
+ }
+ index += getDecodedDataLength();
+ if (index > endIndex) return false;
+
+ /**
+ * 8.4.1.2 Field values
+ * Value Interpretation of First Octet
+ * 0 - 30 This octet is followed by the indicated number (0 - 30)
+ of data octets
+ * 31 This octet is followed by a uintvar, which indicates the number
+ * of data octets after it
+ * 32 - 127 The value is a text string, terminated by a zero octet
+ (NUL character)
+ * 128 - 255 It is an encoded 7-bit value; this header has no more data
+ */
+ byte val = wspData[index];
+ if (0 <= val && val <= WAP_PDU_SHORT_LENGTH_MAX) {
+ index += wspData[index] + 1;
+ } else if (val == WAP_PDU_LENGTH_QUOTE) {
+ if (index + 1 >= endIndex) return false;
+ index++;
+ if (!decodeUintvarInteger(index)) return false;
+ index += getDecodedDataLength();
+ } else if (WAP_PDU_LENGTH_QUOTE < val && val <= 127) {
+ if (!decodeTextString(index)) return false;
+ index += getDecodedDataLength();
+ } else {
+ index++;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ //seek application ID failed. WSP header might be corrupted
+ return false;
+ }
+ return false;
+ }
+
+ /**
* Decode the "X-Wap-Content-URI" type for WSP pdu
*
* @param startIndex The starting position of the "X-Wap-Content-URI" in this pdu
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index c5aa573..15570e4 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -45,7 +45,7 @@
mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
- mMaxResVersion(NULL), mDebugMode(false), mProduct(NULL),
+ mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
@@ -139,6 +139,8 @@
void setMaxResVersion(const char * val) { mMaxResVersion = val; }
bool getDebugMode() { return mDebugMode; }
void setDebugMode(bool val) { mDebugMode = val; }
+ bool getNonConstantId() { return mNonConstantId; }
+ void setNonConstantId(bool val) { mNonConstantId = val; }
const char* getProduct() const { return mProduct; }
void setProduct(const char * val) { mProduct = val; }
@@ -239,6 +241,7 @@
const char* mCustomPackage;
const char* mMaxResVersion;
bool mDebugMode;
+ bool mNonConstantId;
const char* mProduct;
/* file specification */
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 739b01f..266a02f 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -160,7 +160,11 @@
" product variants\n"
" --utf16\n"
" changes default encoding for resources to UTF-16. Only useful when API\n"
- " level is set to 7 or higher where the default encoding is UTF-8.\n");
+ " level is set to 7 or higher where the default encoding is UTF-8.\n"
+ " --non-constant-id\n"
+ " Make the resources ID non constant. This is required to make an R java class\n"
+ " that does not contain the final value but is used to make reusable compiled\n"
+ " libraries that need to access resources.\n");
}
/*
@@ -497,6 +501,8 @@
goto bail;
}
bundle.setProduct(argv[0]);
+ } else if (strcmp(cp, "-non-constant-id") == 0) {
+ bundle.setNonConstantId(true);
} else {
fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
wantUsage = true;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7f84df6..730bd71 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1723,7 +1723,8 @@
static status_t writeSymbolClass(
FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
- const sp<AaptSymbols>& symbols, const String8& className, int indent)
+ const sp<AaptSymbols>& symbols, const String8& className, int indent,
+ bool nonConstantId)
{
fprintf(fp, "%spublic %sfinal class %s {\n",
getIndentSpace(indent),
@@ -1733,6 +1734,10 @@
size_t i;
status_t err = NO_ERROR;
+ const char * id_format = nonConstantId ?
+ "%spublic static int %s=0x%08x;\n" :
+ "%spublic static final int %s=0x%08x;\n";
+
size_t N = symbols->getSymbols().size();
for (i=0; i<N; i++) {
const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
@@ -1785,7 +1790,7 @@
if (deprecated) {
fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
}
- fprintf(fp, "%spublic static final int %s=0x%08x;\n",
+ fprintf(fp, id_format,
getIndentSpace(indent),
String8(name).string(), (int)sym.int32Val);
}
@@ -1836,7 +1841,7 @@
if (nclassName == "styleable") {
styleableSymbols = nsymbols;
} else {
- err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent);
+ err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
}
if (err != NO_ERROR) {
return err;
@@ -1907,7 +1912,7 @@
"\n"
"package %s;\n\n", package.string());
- status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0);
+ status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
if (err != NO_ERROR) {
return err;
}