[automerger skipped] Clean up hidden constants am: be4fe78e0a am: 3492f71078
am: 4bd170390a -s ours
am skip reason: change_id I6e0b527d75987825ee70b6f7ac3b42331bc22571 with SHA1 5e17455390 is in history
Change-Id: Ic8a8fc926ca00ab5c49eabb4708a69f91a4168ab
diff --git a/Android.bp b/Android.bp
index c72b5c8..68fd40b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,7 +1,7 @@
// Copyright 2019 The Android Open Source Project
-
-android_app {
- name: "CellBroadcastServiceModule",
+java_defaults {
+ name: "CellBroadcastServiceCommon",
+ min_sdk_version: "29",
srcs: [
"src/**/*.java",
":framework-annotations",
@@ -15,6 +15,25 @@
optimize: {
proguard_flags_files: ["proguard.flags"],
},
+ jarjar_rules: "cellbroadcast-jarjar-rules.txt",
+ plugins: ["java_api_finder"],
+}
+
+android_app {
+ name: "CellBroadcastServiceModule",
+ defaults: ["CellBroadcastServiceCommon"],
+ certificate: "networkstack",
+ manifest: "AndroidManifest.xml",
+}
+
+android_app {
+ name: "CellBroadcastServiceModulePlatform",
+ defaults: ["CellBroadcastServiceCommon"],
+ certificate: "platform",
+ // CellBroadcastServicePlatformModule is a replacement for com.android.cellbroadcast apex
+ // which consists of CellBroadcastServiceModule
+ overrides: ["com.android.cellbroadcast"],
+ manifest: "AndroidManifest_Platform.xml",
}
// used to share common constants between cellbroadcastservice and cellbroadcastreceier
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4dc0b97..cafdf25 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -17,7 +17,9 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- android:sharedUserId="android.uid.phone"
+ android:sharedUserId="android.uid.networkstack"
+ android:versionCode="300000000"
+ android:versionName="R-initial"
package="com.android.cellbroadcastservice">
<original-package android:name="com.android.cellbroadcastservice" />
@@ -29,15 +31,18 @@
<uses-permission android:name="android.permission.READ_CELL_BROADCASTS" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.BROADCAST_SMS" />
<uses-sdk android:minSdkVersion="29"/>
<application android:label="Module used to handle cell broadcasts."
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
- android:process="com.android.phone">
+ android:persistent="true"
+ android:process="com.android.networkstack.process">
<service android:name="DefaultCellBroadcastService"
+ android:process="com.android.networkstack.process"
android:exported="true"
android:permission="android.permission.BIND_CELL_BROADCAST_SERVICE">
<intent-filter>
diff --git a/AndroidManifest_Platform.xml b/AndroidManifest_Platform.xml
new file mode 100644
index 0000000..4ff8ed5
--- /dev/null
+++ b/AndroidManifest_Platform.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:sharedUserId="android.uid.phone"
+ package="com.android.cellbroadcastservice">
+
+ <original-package android:name="com.android.cellbroadcastservice" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_CELL_BROADCASTS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.BROADCAST_SMS" />
+
+ <uses-sdk android:minSdkVersion="29"/>
+
+ <application android:label="Module used to handle cell broadcasts."
+ android:defaultToDeviceProtectedStorage="true"
+ android:persistent="true"
+ android:directBootAware="true"
+ android:process="com.android.phone">
+
+ <service android:name="DefaultCellBroadcastService"
+ android:process="com.android.phone"
+ android:exported="true"
+ android:permission="android.permission.BIND_CELL_BROADCAST_SERVICE">
+ <intent-filter>
+ <action android:name="android.telephony.CellBroadcastService" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..f69421d
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,50 @@
+# 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/*)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/CellBroadcastServiceModule)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..77f6a76
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,17 @@
+{
+ "presubmit": [
+ {
+ "name": "CellBroadcastServiceTests",
+ "options": [
+ {
+ "include-annotation": "org.junit.Test"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CellBroadcastServiceTests"
+ }
+ ]
+}
diff --git a/cellbroadcast-jarjar-rules.txt b/cellbroadcast-jarjar-rules.txt
index b50ec50..9d34267 100644
--- a/cellbroadcast-jarjar-rules.txt
+++ b/cellbroadcast-jarjar-rules.txt
@@ -1,4 +1 @@
-rule android.util.LocalLog* com.android.cellbroadcast.LocalLog@1
-rule android.util.Slog* com.android.cellbroadcast.Slog@1
-rule com.android.internal.util.State* com.android.cellbroadcastservice.State@1
-rule com.android.internal.util.StateMachine* com.android.cellbroadcastservice.StateMachine@1
+rule android.util.LocalLog* com.android.cellbroadcastservice.LocalLog@1
diff --git a/res/values/config.xml b/res/values/config.xml
index 4d640eb..23c8177 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -24,6 +24,10 @@
<item>com.android.cellbroadcastreceiver</item>
</string-array>
+ <!-- Package names of the test cell broadcast receivers, only used on debug builds -->
+ <string-array name="config_testCellBroadcastReceiverPkgs" translatable="false">
+ </string-array>
+
<!-- The message expiration time in milliseconds for duplicate detection -->
<integer name="message_expiration_time">86400000</integer>
diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml
new file mode 100644
index 0000000..85ba114
--- /dev/null
+++ b/res/values/overlayable.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- These values can be used to control CellBroadcastService behavior on individual devices.
+ These can be overridden by OEM's by using an RRO overlay app. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <overlayable name="CellBroadcastServiceCustomization">
+ <!-- START VENDOR CUSTOMIZATION -->
+ <policy type="product|system|vendor">
+
+ <!-- Params from config.xml that can be overlayed -->
+ <item type="array" name="config_defaultCellBroadcastReceiverPkgs" />
+ <item type="array" name="config_testCellBroadcastReceiverPkgs" />
+ <!-- Params from config.xml that can be overlayed -->
+ </policy>
+ <!-- END VENDOR CUSTOMIZATION -->
+ </overlayable>
+</resources>
diff --git a/res/values/string.xml b/res/values/string.xml
new file mode 100644
index 0000000..3f4d44e
--- /dev/null
+++ b/res/values/string.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for earthquake -->
+ <string name="etws_primary_default_message_earthquake">Stay calm and seek shelter nearby.</string>
+
+ <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for Tsunami -->
+ <string name="etws_primary_default_message_tsunami">Evacuate immediately from coastal regions and riverside areas to a safer place such as high ground.</string>
+
+ <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for earthquake and Tsunami -->
+ <string name="etws_primary_default_message_earthquake_and_tsunami">Stay calm and seek shelter nearby.</string>
+
+ <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for test -->
+ <string name="etws_primary_default_message_test">Emergency messages test</string>
+
+ <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for others -->
+ <string name="etws_primary_default_message_others"></string>
+</resources>
diff --git a/res/values/symbols.xml b/res/values/symbols.xml
index 867c3a2..2cfb796 100644
--- a/res/values/symbols.xml
+++ b/res/values/symbols.xml
@@ -22,5 +22,13 @@
Can be referenced in java code as: com.android.internal.R.<type>.<name>
and in layout xml as: "@*android:<type>/<name>" -->
- <java-symbol type="array" name="config_defaultCellBroadcastReceiverPkgs" />
+ <java-symbol type="array" name="config_defaultCellBroadcastReceiverPkgs" />
+ <java-symbol type="array" name="config_testCellBroadcastReceiverPkgs" />
+
+ <!-- ETWS primary messages -->
+ <java-symbol type="string" name="etws_primary_default_message_earthquake" />
+ <java-symbol type="string" name="etws_primary_default_message_tsunami" />
+ <java-symbol type="string" name="etws_primary_default_message_earthquake_and_tsunami" />
+ <java-symbol type="string" name="etws_primary_default_message_test" />
+ <java-symbol type="string" name="etws_primary_default_message_others" />
</resources>
diff --git a/src/com/android/cellbroadcastservice/BearerData.java b/src/com/android/cellbroadcastservice/BearerData.java
index b1324d7..d73339c 100644
--- a/src/com/android/cellbroadcastservice/BearerData.java
+++ b/src/com/android/cellbroadcastservice/BearerData.java
@@ -17,11 +17,9 @@
package com.android.cellbroadcastservice;
import android.content.Context;
+import android.telephony.Rlog;
import android.telephony.SmsCbCmasInfo;
import android.telephony.cdma.CdmaSmsCbProgramData;
-import android.util.Log;
-
-import com.android.internal.util.BitwiseInputStream;
/**
* An object to decode CDMA SMS bearer data.
@@ -68,6 +66,13 @@
public static final int LANGUAGE_HEBREW = 0x07;
/**
+ * Supported message types for CDMA SMS messages
+ * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
+ * Used for CdmaSmsCbTest.
+ */
+ public static final int MESSAGE_TYPE_DELIVER = 0x01;
+
+ /**
* 16-bit value indicating the message ID, which increments modulo 65536.
* (Special rules apply for WAP-messages.)
* (See 3GPP2 C.S0015-B, v2, 4.5.1)
@@ -189,9 +194,9 @@
inStream.skip(3);
}
if ((!decodeSuccess) || (paramBits > 0)) {
- Log.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
- (decodeSuccess ? "succeeded" : "failed") +
- " (extra bits = " + paramBits + ")");
+ Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode "
+ + (decodeSuccess ? "succeeded" : "failed")
+ + " (extra bits = " + paramBits + ")");
}
inStream.skip(paramBits);
return decodeSuccess;
@@ -206,7 +211,7 @@
decodeSuccess = true;
inStream.skip(paramBits);
}
- Log.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode "
+ Rlog.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode "
+ (decodeSuccess ? "succeeded" : "failed") + " (param bits = " + paramBits + ")");
if (!decodeSuccess) {
throw new CodingException("RESERVED bearer data subparameter " + subparamId
@@ -258,7 +263,7 @@
if (maxNumFields < 0) {
throw new CodingException(charset + " decode failed: offset out of range");
}
- Log.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = "
+ Rlog.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = "
+ numFields + " data.length = " + data.length + " maxNumFields = "
+ maxNumFields);
numFields = maxNumFields;
@@ -427,9 +432,9 @@
bData.language = inStream.read(8);
}
if ((!decodeSuccess) || (paramBits > 0)) {
- Log.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
- (decodeSuccess ? "succeeded" : "failed") +
- " (extra bits = " + paramBits + ")");
+ Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode "
+ + (decodeSuccess ? "succeeded" : "failed")
+ + " (extra bits = " + paramBits + ")");
}
inStream.skip(paramBits);
return decodeSuccess;
@@ -447,9 +452,9 @@
inStream.skip(6);
}
if ((!decodeSuccess) || (paramBits > 0)) {
- Log.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
- (decodeSuccess ? "succeeded" : "failed") +
- " (extra bits = " + paramBits + ")");
+ Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode "
+ + (decodeSuccess ? "succeeded" : "failed")
+ + " (extra bits = " + paramBits + ")");
}
inStream.skip(paramBits);
return decodeSuccess;
@@ -548,7 +553,7 @@
break;
default:
- Log.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
+ Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
inStream.skip(recordLen * 8);
break;
}
@@ -626,9 +631,9 @@
}
return bData;
} catch (BitwiseInputStream.AccessException ex) {
- Log.e(LOG_TAG, "BearerData decode failed: " + ex);
+ Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
} catch (CodingException ex) {
- Log.e(LOG_TAG, "BearerData decode failed: " + ex);
+ Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
}
return null;
}
diff --git a/src/com/android/cellbroadcastservice/BitwiseInputStream.java b/src/com/android/cellbroadcastservice/BitwiseInputStream.java
new file mode 100644
index 0000000..eb4ed38
--- /dev/null
+++ b/src/com/android/cellbroadcastservice/BitwiseInputStream.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cellbroadcastservice;
+
+/**
+ * An object that provides bitwise incremental read access to a byte array.
+ *
+ * This is useful, for example, when accessing a series of fields that
+ * may not be aligned on byte boundaries.
+ *
+ * NOTE -- This class is not threadsafe.
+ */
+public class BitwiseInputStream {
+
+ // The byte array being read from.
+ private byte[] mBuf;
+
+ // The current position offset, in bits, from the msb in byte 0.
+ private int mPos;
+
+ // The last valid bit offset.
+ private int mEnd;
+
+ /**
+ * An exception to report access problems.
+ */
+ public static class AccessException extends Exception {
+ public AccessException(String s) {
+ super("BitwiseInputStream access failed: " + s);
+ }
+ }
+
+ /**
+ * Create object from byte array.
+ *
+ * @param buf a byte array containing data
+ */
+ public BitwiseInputStream(byte[] buf) {
+ mBuf = buf;
+ mEnd = buf.length << 3;
+ mPos = 0;
+ }
+
+ /**
+ * Return the number of bit still available for reading.
+ */
+ public int available() {
+ return mEnd - mPos;
+ }
+
+ /**
+ * Read some data and increment the current position.
+ *
+ * The 8-bit limit on access to bitwise streams is intentional to
+ * avoid endianness issues.
+ *
+ * @param bits the amount of data to read (gte 0, lte 8)
+ * @return byte of read data (possibly partially filled, from lsb)
+ */
+ public int read(int bits) throws AccessException {
+ int index = mPos >>> 3;
+ int offset = 16 - (mPos & 0x07) - bits; // &7==%8
+ if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) {
+ throw new AccessException(
+ "illegal read (pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
+ }
+ int data = (mBuf[index] & 0xFF) << 8;
+ if (offset < 8) data |= mBuf[index + 1] & 0xFF;
+ data >>>= offset;
+ data &= (-1 >>> (32 - bits));
+ mPos += bits;
+ return data;
+ }
+
+ /**
+ * Read data in bulk into a byte array and increment the current position.
+ *
+ * @param bits the amount of data to read
+ * @return newly allocated byte array of read data
+ */
+ public byte[] readByteArray(int bits) throws AccessException {
+ int bytes = (bits >>> 3) + ((bits & 0x07) > 0 ? 1 : 0); // &7==%8
+ byte[] arr = new byte[bytes];
+ for (int i = 0; i < bytes; i++) {
+ int increment = Math.min(8, bits - (i << 3));
+ arr[i] = (byte) (read(increment) << (8 - increment));
+ }
+ return arr;
+ }
+
+ /**
+ * Increment the current position and ignore contained data.
+ *
+ * @param bits the amount by which to increment the position
+ */
+ public void skip(int bits) throws AccessException {
+ if ((mPos + bits) > mEnd) {
+ throw new AccessException(
+ "illegal skip (pos " + mPos + ", end " + mEnd + ", bits " + bits + ")");
+ }
+ mPos += bits;
+ }
+}
diff --git a/src/com/android/cellbroadcastservice/CbGeoUtils.java b/src/com/android/cellbroadcastservice/CbGeoUtils.java
index 0a8fe48..2a7a1d7 100644
--- a/src/com/android/cellbroadcastservice/CbGeoUtils.java
+++ b/src/com/android/cellbroadcastservice/CbGeoUtils.java
@@ -19,8 +19,8 @@
import android.annotation.NonNull;
import android.telephony.CbGeoUtils.Geometry;
import android.telephony.CbGeoUtils.LatLng;
+import android.telephony.Rlog;
import android.text.TextUtils;
-import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -231,7 +231,7 @@
geometries.add(new Polygon(vertices));
break;
default:
- Log.e(TAG, "Invalid geometry format " + geometryStr);
+ Rlog.e(TAG, "Invalid geometry format " + geometryStr);
}
}
return geometries;
@@ -284,7 +284,7 @@
sb.append("|");
sb.append(circle.getRadius());
} else {
- Log.e(TAG, "Unsupported geometry object " + geometry);
+ Rlog.e(TAG, "Unsupported geometry object " + geometry);
return null;
}
return sb.toString();
diff --git a/src/com/android/cellbroadcastservice/CdmaServiceCategoryProgramHandler.java b/src/com/android/cellbroadcastservice/CdmaServiceCategoryProgramHandler.java
index 212962e..4b73258 100644
--- a/src/com/android/cellbroadcastservice/CdmaServiceCategoryProgramHandler.java
+++ b/src/com/android/cellbroadcastservice/CdmaServiceCategoryProgramHandler.java
@@ -139,7 +139,7 @@
// TODO: move this resource and its overlays to the CellBroadcastService directory
String[] pkgs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_defaultCellBroadcastReceiverPkgs);
+ R.array.config_defaultCellBroadcastReceiverPkgs);
mReceiverCount.addAndGet(pkgs.length);
for (String pkg : pkgs) {
intent.setPackage(pkg);
diff --git a/src/com/android/cellbroadcastservice/CellBroadcastHandler.java b/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
index cc0aba4..f22bd08 100644
--- a/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
+++ b/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
@@ -16,7 +16,8 @@
package com.android.cellbroadcastservice;
-import static android.provider.Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import android.Manifest;
import android.annotation.NonNull;
@@ -32,29 +33,29 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Location;
-import android.location.LocationListener;
import android.location.LocationManager;
+import android.location.LocationRequest;
import android.net.Uri;
-import android.os.Bundle;
+import android.os.Build;
+import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.provider.Settings;
import android.provider.Telephony;
import android.provider.Telephony.CellBroadcasts;
import android.telephony.CbGeoUtils.Geometry;
import android.telephony.CbGeoUtils.LatLng;
+import android.telephony.Rlog;
import android.telephony.SmsCbMessage;
import android.telephony.SubscriptionManager;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.LocalLog;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,6 +68,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -99,7 +101,7 @@
private static final boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1;
/** Uses to request the location update. */
- public final LocationRequester mLocationRequester;
+ private final LocationRequester mLocationRequester;
/** Timestamp of last airplane mode on */
protected long mLastAirplaneModeTime = 0;
@@ -120,12 +122,13 @@
this("CellBroadcastHandler", context, Looper.myLooper());
}
- protected CellBroadcastHandler(String debugTag, Context context, Looper looper) {
+ @VisibleForTesting
+ public CellBroadcastHandler(String debugTag, Context context, Looper looper) {
super(debugTag, context, looper);
mLocationRequester = new LocationRequester(
context,
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE),
- getHandler().getLooper());
+ getHandler());
// Adding GSM / CDMA service category mapping.
mServiceCategoryCrossRATMap = Stream.of(new Integer[][] {
@@ -186,7 +189,7 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- if (IS_DEBUGGABLE) {
+ if (Build.IS_DEBUGGABLE) {
intentFilter.addAction(ACTION_DUPLICATE_DETECTION);
}
mContext.registerReceiver(
@@ -322,10 +325,7 @@
List<SmsCbMessage> cbMessages = new ArrayList<>();
try (Cursor cursor = mContext.getContentResolver().query(CellBroadcasts.CONTENT_URI,
- // TODO: QUERY_COLUMNS_FWK is a hidden API, since we are going to move
- // CellBroadcastProvider to this module we can define those COLUMNS in side
- // CellBroadcastProvider and reference from there.
- CellBroadcasts.QUERY_COLUMNS_FWK,
+ CellBroadcastProvider.QUERY_COLUMNS,
where,
new String[] {Long.toString(dupCheckTime)},
null)) {
@@ -484,26 +484,31 @@
if (IS_DEBUGGABLE) {
// Send additional broadcast intent to the specified package. This is only for sl4a
// automation tests.
- final String additionalPackage = Settings.Secure.getString(
- mContext.getContentResolver(), CMAS_ADDITIONAL_BROADCAST_PKG);
- if (additionalPackage != null) {
+ String[] testPkgs = mContext.getResources().getStringArray(
+ R.array.config_testCellBroadcastReceiverPkgs);
+ if (testPkgs != null) {
+ mReceiverCount.addAndGet(testPkgs.length);
Intent additionalIntent = new Intent(intent);
- additionalIntent.setPackage(additionalPackage);
- mContext.createContextAsUser(UserHandle.ALL, 0).sendOrderedBroadcast(
- additionalIntent, receiverPermission, appOp, null, getHandler(),
- Activity.RESULT_OK, null, null);
+ for (String pkg : testPkgs) {
+ additionalIntent.setPackage(pkg);
+ mContext.createContextAsUser(UserHandle.ALL, 0).sendOrderedBroadcast(
+ additionalIntent, receiverPermission, appOp, mReceiver,
+ getHandler(), Activity.RESULT_OK, null, null);
+ }
}
}
String[] pkgs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_defaultCellBroadcastReceiverPkgs);
- mReceiverCount.addAndGet(pkgs.length);
- for (String pkg : pkgs) {
- // Explicitly send the intent to all the configured cell broadcast receivers.
- intent.setPackage(pkg);
- mContext.createContextAsUser(UserHandle.ALL, 0).sendOrderedBroadcast(
- intent, receiverPermission, appOp, null, getHandler(),
- Activity.RESULT_OK, null, null);
+ R.array.config_defaultCellBroadcastReceiverPkgs);
+ if (pkgs != null) {
+ mReceiverCount.addAndGet(pkgs.length);
+ for (String pkg : pkgs) {
+ // Explicitly send the intent to all the configured cell broadcast receivers.
+ intent.setPackage(pkg);
+ mContext.createContextAsUser(UserHandle.ALL, 0).sendOrderedBroadcast(
+ intent, receiverPermission, appOp, mReceiver, getHandler(),
+ Activity.RESULT_OK, null, null);
+ }
}
} else {
msg = "Dispatching SMS CB, SmsCbMessage is: " + message;
@@ -578,18 +583,9 @@
/**
* Use as the default maximum wait time if the cell broadcast doesn't specify the value.
- * Most of the location request should be responded within 20 seconds.
+ * Most of the location request should be responded within 30 seconds.
*/
- private static final int DEFAULT_MAXIMUM_WAIT_TIME_SEC = 20;
-
- /**
- * Trigger this event when the {@link LocationManager} is not responded within the given
- * time.
- */
- private static final int EVENT_LOCATION_REQUEST_TIMEOUT = 1;
-
- /** Request a single location update. */
- private static final int EVENT_REQUEST_LOCATION_UPDATE = 2;
+ private static final int DEFAULT_MAXIMUM_WAIT_TIME_SEC = 30;
/**
* Request location update from network or gps location provider. Network provider will be
@@ -599,17 +595,20 @@
LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER);
private final LocationManager mLocationManager;
- private final Looper mLooper;
private final List<LocationUpdateCallback> mCallbacks;
private final Context mContext;
- private Handler mLocationHandler;
+ private final Handler mLocationHandler;
- LocationRequester(Context context, LocationManager locationManager, Looper looper) {
+ private int mNumLocationUpdatesInProgress;
+
+ private final List<CancellationSignal> mCancellationSignals = new ArrayList<>();
+
+ LocationRequester(Context context, LocationManager locationManager, Handler handler) {
mLocationManager = locationManager;
- mLooper = looper;
mCallbacks = new ArrayList<>();
mContext = context;
- mLocationHandler = new LocationHandler(looper);
+ mLocationHandler = handler;
+ mNumLocationUpdatesInProgress = 0;
}
/**
@@ -617,103 +616,83 @@
* {@code null} location will be called immediately.
*
* @param callback a callback to the response when the location is available
- * @param maximumWaitTimeSec the maximum wait time of this request. If location is not
+ * @param maximumWaitTimeS the maximum wait time of this request. If location is not
* updated within the maximum wait time, {@code callback#onLocationUpadte(null)} will be
* called.
*/
void requestLocationUpdate(@NonNull LocationUpdateCallback callback,
- int maximumWaitTimeSec) {
- mLocationHandler.obtainMessage(EVENT_REQUEST_LOCATION_UPDATE, maximumWaitTimeSec,
- 0 /* arg2 */, callback).sendToTarget();
+ int maximumWaitTimeS) {
+ mLocationHandler.post(() -> requestLocationUpdateInternal(callback, maximumWaitTimeS));
}
- private void onLocationUpdate(@Nullable LatLng location) {
+ private void onLocationUpdate(@Nullable Location location) {
+ mNumLocationUpdatesInProgress--;
+
+ LatLng latLng = null;
+ if (location != null) {
+ Rlog.d(TAG, "Got location update");
+ latLng = new LatLng(location.getLatitude(), location.getLongitude());
+ } else if (mNumLocationUpdatesInProgress > 0) {
+ Rlog.d(TAG, "Still waiting for " + mNumLocationUpdatesInProgress
+ + " more location updates.");
+ return;
+ } else {
+ Rlog.d(TAG, "Location is not available.");
+ }
+
for (LocationUpdateCallback callback : mCallbacks) {
- callback.onLocationUpdate(location);
+ callback.onLocationUpdate(latLng);
}
mCallbacks.clear();
+
+ mCancellationSignals.forEach(CancellationSignal::cancel);
+ mCancellationSignals.clear();
+
+ mNumLocationUpdatesInProgress = 0;
}
private void requestLocationUpdateInternal(@NonNull LocationUpdateCallback callback,
- int maximumWaitTimeSec) {
- if (DBG) Log.d(TAG, "requestLocationUpdate");
- if (!isLocationServiceAvailable()) {
+ int maximumWaitTimeS) {
+ if (DBG) Rlog.d(TAG, "requestLocationUpdate");
+ if (!hasPermission(ACCESS_FINE_LOCATION) && !hasPermission(ACCESS_COARSE_LOCATION)) {
if (DBG) {
- Log.d(TAG, "Can't request location update because of no location permission");
+ Rlog.d(TAG, "Can't request location update because of no location permission");
}
callback.onLocationUpdate(null);
return;
}
+ if (mNumLocationUpdatesInProgress == 0) {
+ for (String provider : LOCATION_PROVIDERS) {
+ if (!mLocationManager.isProviderEnabled(provider)) {
+ if (DBG) {
+ Rlog.d(TAG, "provider " + provider + " not available");
+ }
+ continue;
+ }
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(provider,
+ 0, 0, true);
+ if (maximumWaitTimeS == SmsCbMessage.MAXIMUM_WAIT_TIME_NOT_SET) {
+ maximumWaitTimeS = DEFAULT_MAXIMUM_WAIT_TIME_SEC;
+ }
+ request.setExpireIn(TimeUnit.SECONDS.toMillis(maximumWaitTimeS));
- if (!mLocationHandler.hasMessages(EVENT_LOCATION_REQUEST_TIMEOUT)) {
- if (maximumWaitTimeSec == SmsCbMessage.MAXIMUM_WAIT_TIME_NOT_SET) {
- maximumWaitTimeSec = DEFAULT_MAXIMUM_WAIT_TIME_SEC;
- }
- mLocationHandler.sendMessageDelayed(
- mLocationHandler.obtainMessage(EVENT_LOCATION_REQUEST_TIMEOUT),
- maximumWaitTimeSec * DateUtils.SECOND_IN_MILLIS);
- }
-
- mCallbacks.add(callback);
-
- for (String provider : LOCATION_PROVIDERS) {
- if (mLocationManager.isProviderEnabled(provider)) {
- mLocationManager.requestSingleUpdate(provider, mLocationListener, mLooper);
- break;
+ CancellationSignal signal = new CancellationSignal();
+ mCancellationSignals.add(signal);
+ mLocationManager.getCurrentLocation(request, signal,
+ new HandlerExecutor(mLocationHandler), this::onLocationUpdate);
+ mNumLocationUpdatesInProgress++;
}
}
- }
-
- private boolean isLocationServiceAvailable() {
- if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- && !hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) return false;
- for (String provider : LOCATION_PROVIDERS) {
- if (mLocationManager.isProviderEnabled(provider)) return true;
+ if (mNumLocationUpdatesInProgress > 0) {
+ mCallbacks.add(callback);
+ } else {
+ callback.onLocationUpdate(null);
}
- return false;
}
private boolean hasPermission(String permission) {
return mContext.checkPermission(permission, Process.myPid(), Process.myUid())
== PackageManager.PERMISSION_GRANTED;
}
-
- private final LocationListener mLocationListener = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- mLocationHandler.removeMessages(EVENT_LOCATION_REQUEST_TIMEOUT);
- onLocationUpdate(new LatLng(location.getLatitude(), location.getLongitude()));
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {}
-
- @Override
- public void onProviderEnabled(String provider) {}
-
- @Override
- public void onProviderDisabled(String provider) {}
- };
-
- private final class LocationHandler extends Handler {
- LocationHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_LOCATION_REQUEST_TIMEOUT:
- if (DBG) Log.d(TAG, "location request timeout");
- onLocationUpdate(null);
- break;
- case EVENT_REQUEST_LOCATION_UPDATE:
- requestLocationUpdateInternal((LocationUpdateCallback) msg.obj, msg.arg1);
- break;
- default:
- Log.e(TAG, "Unsupported message type " + msg.what);
- }
- }
- }
}
}
diff --git a/src/com/android/cellbroadcastservice/CellBroadcastProvider.java b/src/com/android/cellbroadcastservice/CellBroadcastProvider.java
index 8b1a747..1369b52 100644
--- a/src/com/android/cellbroadcastservice/CellBroadcastProvider.java
+++ b/src/com/android/cellbroadcastservice/CellBroadcastProvider.java
@@ -31,6 +31,7 @@
import android.os.Binder;
import android.os.Process;
import android.provider.Telephony.CellBroadcasts;
+import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.Log;
@@ -98,6 +99,37 @@
/** Content uri of this provider. */
public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
+ /**
+ * Local definition of the query columns for instantiating
+ * {@link android.telephony.SmsCbMessage} objects.
+ */
+ public static final String[] QUERY_COLUMNS = {
+ CellBroadcasts._ID,
+ CellBroadcasts.SLOT_INDEX,
+ CellBroadcasts.SUB_ID,
+ CellBroadcasts.GEOGRAPHICAL_SCOPE,
+ CellBroadcasts.PLMN,
+ CellBroadcasts.LAC,
+ CellBroadcasts.CID,
+ CellBroadcasts.SERIAL_NUMBER,
+ CellBroadcasts.SERVICE_CATEGORY,
+ CellBroadcasts.LANGUAGE_CODE,
+ CellBroadcasts.MESSAGE_BODY,
+ CellBroadcasts.MESSAGE_FORMAT,
+ CellBroadcasts.MESSAGE_PRIORITY,
+ CellBroadcasts.ETWS_WARNING_TYPE,
+ CellBroadcasts.CMAS_MESSAGE_CLASS,
+ CellBroadcasts.CMAS_CATEGORY,
+ CellBroadcasts.CMAS_RESPONSE_TYPE,
+ CellBroadcasts.CMAS_SEVERITY,
+ CellBroadcasts.CMAS_URGENCY,
+ CellBroadcasts.CMAS_CERTAINTY,
+ CellBroadcasts.RECEIVED_TIME,
+ CellBroadcasts.MESSAGE_BROADCASTED,
+ CellBroadcasts.GEOMETRIES,
+ CellBroadcasts.MAXIMUM_WAIT_TIME
+ };
+
@VisibleForTesting
public PermissionChecker mPermissionChecker;
@@ -148,7 +180,7 @@
checkReadPermission(uri);
if (DBG) {
- Log.d(TAG, "query:"
+ Rlog.d(TAG, "query:"
+ " uri = " + uri
+ " projection = " + Arrays.toString(projection)
+ " selection = " + selection
@@ -188,7 +220,7 @@
checkWritePermission();
if (DBG) {
- Log.d(TAG, "insert:"
+ Rlog.d(TAG, "insert:"
+ " uri = " + uri
+ " contentValue = " + values);
}
@@ -203,7 +235,7 @@
.notifyChange(CONTENT_URI, null /* observer */);
return newUri;
} else {
- Log.e(TAG, "Insert record failed because of unknown reason, uri = " + uri);
+ Rlog.e(TAG, "Insert record failed because of unknown reason, uri = " + uri);
return null;
}
default:
@@ -217,7 +249,7 @@
checkWritePermission();
if (DBG) {
- Log.d(TAG, "delete:"
+ Rlog.d(TAG, "delete:"
+ " uri = " + uri
+ " selection = " + selection
+ " selectionArgs = " + Arrays.toString(selectionArgs));
@@ -238,7 +270,7 @@
checkWritePermission();
if (DBG) {
- Log.d(TAG, "update:"
+ Rlog.d(TAG, "update:"
+ " uri = " + uri
+ " values = {" + values + "}"
+ " selection = " + selection
@@ -345,12 +377,12 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (DBG) {
- Log.d(TAG, "onUpgrade: oldV=" + oldVersion + " newV=" + newVersion);
+ Rlog.d(TAG, "onUpgrade: oldV=" + oldVersion + " newV=" + newVersion);
}
- if (newVersion == 2) {
+ if (oldVersion < 2) {
db.execSQL("ALTER TABLE " + CELL_BROADCASTS_TABLE_NAME + " ADD COLUMN "
+ CellBroadcasts.SLOT_INDEX + " INTEGER DEFAULT 0;");
- Log.d(TAG, "add slotIndex column");
+ Rlog.d(TAG, "add slotIndex column");
}
}
}
diff --git a/src/com/android/cellbroadcastservice/DefaultCellBroadcastService.java b/src/com/android/cellbroadcastservice/DefaultCellBroadcastService.java
index fdf3b4a..3ae61bb 100644
--- a/src/com/android/cellbroadcastservice/DefaultCellBroadcastService.java
+++ b/src/com/android/cellbroadcastservice/DefaultCellBroadcastService.java
@@ -19,12 +19,14 @@
import android.content.Context;
import android.os.Bundle;
import android.telephony.CellBroadcastService;
+import android.telephony.Rlog;
import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaSmsCbProgramData;
-import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
@@ -57,13 +59,13 @@
@Override
public void onGsmCellBroadcastSms(int slotIndex, byte[] message) {
- Log.d(TAG, "onGsmCellBroadcastSms received message on slotId=" + slotIndex);
+ Rlog.d(TAG, "onGsmCellBroadcastSms received message on slotId=" + slotIndex);
mGsmCellBroadcastHandler.onGsmCellBroadcastSms(slotIndex, message);
}
@Override
public void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData, int serviceCategory) {
- Log.d(TAG, "onCdmaCellBroadcastSms received message on slotId=" + slotIndex);
+ Rlog.d(TAG, "onCdmaCellBroadcastSms received message on slotId=" + slotIndex);
int[] subIds =
((SubscriptionManager) getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE)).getSubscriptionIds(slotIndex);
@@ -76,7 +78,8 @@
} else {
plmn = "";
}
- SmsCbMessage message = parseBroadcastSms(slotIndex, plmn, bearerData, serviceCategory);
+ SmsCbMessage message = parseBroadcastSms(getApplicationContext(), slotIndex, plmn,
+ bearerData, serviceCategory);
if (message != null) {
mCdmaCellBroadcastHandler.onCdmaCellBroadcastSms(message);
}
@@ -85,12 +88,11 @@
@Override
public void onCdmaScpMessage(int slotIndex, List<CdmaSmsCbProgramData> programData,
String originatingAddress, Consumer<Bundle> callback) {
- Log.d(TAG, "onCdmaScpMessage received message on slotId=" + slotIndex);
+ Rlog.d(TAG, "onCdmaScpMessage received message on slotId=" + slotIndex);
mCdmaScpHandler.onCdmaScpMessage(slotIndex, new ArrayList<>(programData),
originatingAddress, callback);
}
-
/**
* Parses a CDMA broadcast SMS
*
@@ -99,20 +101,30 @@
* @param bearerData the bearerData of the SMS
* @param serviceCategory the service category of the broadcast
*/
- private SmsCbMessage parseBroadcastSms(int slotIndex, String plmn, byte[] bearerData,
+ @VisibleForTesting
+ public static SmsCbMessage parseBroadcastSms(Context context, int slotIndex, String plmn,
+ byte[] bearerData,
int serviceCategory) {
- BearerData bData = BearerData.decode(getApplicationContext(), bearerData, serviceCategory);
+ BearerData bData = BearerData.decode(context, bearerData, serviceCategory);
if (bData == null) {
- Log.w(TAG, "BearerData.decode() returned null");
+ Rlog.w(TAG, "BearerData.decode() returned null");
return null;
}
- Log.d(TAG, "MT raw BearerData = " + toHexString(bearerData, 0, bearerData.length));
+ Rlog.d(TAG, "MT raw BearerData = " + toHexString(bearerData, 0, bearerData.length));
SmsCbLocation location = new SmsCbLocation(plmn);
+ SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ int[] subIds = sm.getSubscriptionIds(slotIndex);
+ if (subIds != null && subIds.length > 0) {
+ subId = subIds[0];
+ }
+
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, bData.messageId, location,
serviceCategory, bData.getLanguage(), bData.userData.payloadStr,
- bData.priority, null, bData.cmasWarningInfo, slotIndex);
+ bData.priority, null, bData.cmasWarningInfo, slotIndex, subId);
}
private static String toHexString(byte[] array, int offset, int length) {
diff --git a/src/com/android/cellbroadcastservice/GsmAlphabet.java b/src/com/android/cellbroadcastservice/GsmAlphabet.java
index aecaf1b..c305557 100644
--- a/src/com/android/cellbroadcastservice/GsmAlphabet.java
+++ b/src/com/android/cellbroadcastservice/GsmAlphabet.java
@@ -17,7 +17,7 @@
package com.android.cellbroadcastservice;
import android.annotation.UnsupportedAppUsage;
-import android.util.Log;
+import android.telephony.Rlog;
import android.util.SparseIntArray;
/**
@@ -584,7 +584,7 @@
int numTables = sLanguageTables.length;
int numShiftTables = sLanguageShiftTables.length;
if (numTables != numShiftTables) {
- Log.e(TAG, "Error: language tables array length " + numTables
+ Rlog.e(TAG, "Error: language tables array length " + numTables
+ " != shift tables array length " + numShiftTables);
}
@@ -594,7 +594,7 @@
int tableLen = table.length();
if (tableLen != 0 && tableLen != 128) {
- Log.e(TAG, "Error: language tables index " + i + " length " + tableLen
+ Rlog.e(TAG, "Error: language tables index " + i + " length " + tableLen
+ " (expected 128 or 0)");
}
@@ -612,7 +612,7 @@
int shiftTableLen = shiftTable.length();
if (shiftTableLen != 0 && shiftTableLen != 128) {
- Log.e(TAG, "Error: language shift tables index " + i + " length " + shiftTableLen
+ Rlog.e(TAG, "Error: language shift tables index " + i + " length " + shiftTableLen
+ " (expected 128 or 0)");
}
@@ -666,11 +666,11 @@
StringBuilder ret = new StringBuilder(lengthSeptets);
if (languageTable < 0 || languageTable > sLanguageTables.length) {
- Log.w(TAG, "unknown language table " + languageTable + ", using default");
+ Rlog.w(TAG, "unknown language table " + languageTable + ", using default");
languageTable = 0;
}
if (shiftTable < 0 || shiftTable > sLanguageShiftTables.length) {
- Log.w(TAG, "unknown single shift table " + shiftTable + ", using default");
+ Rlog.w(TAG, "unknown single shift table " + shiftTable + ", using default");
shiftTable = 0;
}
@@ -680,11 +680,11 @@
String shiftTableToChar = sLanguageShiftTables[shiftTable];
if (languageTableToChar.isEmpty()) {
- Log.w(TAG, "no language table for code " + languageTable + ", using default");
+ Rlog.w(TAG, "no language table for code " + languageTable + ", using default");
languageTableToChar = sLanguageTables[0];
}
if (shiftTableToChar.isEmpty()) {
- Log.w(TAG, "no single shift table for code " + shiftTable + ", using default");
+ Rlog.w(TAG, "no single shift table for code " + shiftTable + ", using default");
shiftTableToChar = sLanguageShiftTables[0];
}
@@ -724,7 +724,7 @@
}
}
} catch (RuntimeException ex) {
- Log.e(TAG, "Error GSM 7 bit packed: ", ex);
+ Rlog.e(TAG, "Error GSM 7 bit packed: ", ex);
return null;
}
diff --git a/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java b/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java
index 2f16555..f2fbec5 100644
--- a/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java
+++ b/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java
@@ -61,7 +61,7 @@
new HashMap<>(4);
@VisibleForTesting
- protected GsmCellBroadcastHandler(Context context, Looper looper) {
+ public GsmCellBroadcastHandler(Context context, Looper looper) {
super("GsmCellBroadcastHandler", context, looper);
}
@@ -131,7 +131,7 @@
ContentResolver resolver = mContext.getContentResolver();
for (CellBroadcastIdentity identity : geoFencingTriggerMessage.cbIdentifiers) {
try (Cursor cursor = resolver.query(CellBroadcasts.CONTENT_URI,
- CellBroadcasts.QUERY_COLUMNS_FWK,
+ CellBroadcastProvider.QUERY_COLUMNS,
where,
new String[] { Integer.toString(identity.messageIdentifier),
Integer.toString(identity.serialNumber), MESSAGE_NOT_BROADCASTED,
diff --git a/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java b/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
index d17c084..8c65c49 100644
--- a/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
+++ b/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
@@ -27,17 +27,18 @@
import android.content.res.Resources;
import android.telephony.CbGeoUtils.Geometry;
import android.telephony.CbGeoUtils.LatLng;
+import android.telephony.Rlog;
import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
import android.util.Pair;
-import android.util.Slog;
import com.android.cellbroadcastservice.CbGeoUtils.Circle;
import com.android.cellbroadcastservice.CbGeoUtils.Polygon;
import com.android.cellbroadcastservice.GsmSmsCbMessage.GeoFencingTriggerMessage.CellBroadcastIdentity;
import com.android.cellbroadcastservice.SmsCbHeader.DataCodingScheme;
-import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
@@ -66,7 +67,8 @@
* @param category ETWS message category defined in SmsCbConstants
* @return ETWS text message in string. Return an empty string if no match.
*/
- private static String getEtwsPrimaryMessage(Context context, int category) {
+ @VisibleForTesting
+ public static String getEtwsPrimaryMessage(Context context, int category) {
final Resources r = context.getResources();
switch (category) {
case ETWS_WARNING_TYPE_EARTHQUAKE:
@@ -92,6 +94,14 @@
public static SmsCbMessage createSmsCbMessage(Context context, SmsCbHeader header,
SmsCbLocation location, byte[][] pdus, int slotIndex)
throws IllegalArgumentException {
+ SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ int[] subIds = sm.getSubscriptionIds(slotIndex);
+ if (subIds != null && subIds.length > 0) {
+ subId = subIds[0];
+ }
+
long receivedTimeMillis = System.currentTimeMillis();
if (header.isEtwsPrimaryNotification()) {
// ETSI TS 23.041 ETWS Primary Notification message
@@ -102,7 +112,7 @@
header.getSerialNumber(), location, header.getServiceCategory(), null,
getEtwsPrimaryMessage(context, header.getEtwsInfo().getWarningType()),
SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, header.getEtwsInfo(),
- header.getCmasInfo(), slotIndex);
+ header.getCmasInfo(), slotIndex, subId);
} else if (header.isUmtsFormat()) {
// UMTS format has only 1 PDU
byte[] pdu = pdus[0];
@@ -128,7 +138,7 @@
} catch (Exception ex) {
// Catch the exception here, the message will be considered as having no WAC
// information which means the message will be broadcasted directly.
- Slog.e(TAG, "Can't parse warning area coordinates, ex = " + ex.toString());
+ Rlog.e(TAG, "Can't parse warning area coordinates, ex = " + ex.toString());
}
}
@@ -136,7 +146,7 @@
header.getGeographicalScope(), header.getSerialNumber(), location,
header.getServiceCategory(), language, body, priority,
header.getEtwsInfo(), header.getCmasInfo(), maximumWaitingTimeSec, geometries,
- receivedTimeMillis, slotIndex);
+ receivedTimeMillis, slotIndex, subId);
} else {
String language = null;
StringBuilder sb = new StringBuilder();
@@ -151,7 +161,7 @@
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
header.getGeographicalScope(), header.getSerialNumber(), location,
header.getServiceCategory(), language, sb.toString(), priority,
- header.getEtwsInfo(), header.getCmasInfo(), slotIndex);
+ header.getEtwsInfo(), header.getCmasInfo(), slotIndex, subId);
}
}
@@ -194,7 +204,7 @@
}
return new GeoFencingTriggerMessage(type, cbIdentifiers);
} catch (Exception ex) {
- Slog.e(TAG, "create geo-fencing trigger failed, ex = " + ex.toString());
+ Rlog.e(TAG, "create geo-fencing trigger failed, ex = " + ex.toString());
return null;
}
}
@@ -488,7 +498,11 @@
return type == TYPE_ACTIVE_ALERT_SHARE_WAC;
}
- static final class CellBroadcastIdentity {
+ /**
+ * The GSM cell broadcast identity
+ */
+ @VisibleForTesting
+ public static final class CellBroadcastIdentity {
public final int messageIdentifier;
public final int serialNumber;
CellBroadcastIdentity(int messageIdentifier, int serialNumber) {
diff --git a/src/com/android/cellbroadcastservice/StateMachine.java b/src/com/android/cellbroadcastservice/StateMachine.java
index 1301ea8..74d16a21 100644
--- a/src/com/android/cellbroadcastservice/StateMachine.java
+++ b/src/com/android/cellbroadcastservice/StateMachine.java
@@ -21,6 +21,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.Log;
@@ -1234,8 +1235,8 @@
/** @see StateMachine#transitionTo(IState) */
private final void transitionTo(IState destState) {
if (mTransitionInProgress) {
- Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
- mDestState + ", new target state=" + destState);
+ Log.wtf(mSm.mName, "transitionTo called while transition already in progress to "
+ + mDestState + ", new target state=" + destState);
}
mDestState = (State) destState;
if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
@@ -2123,7 +2124,7 @@
* @param s is string log
*/
protected void log(String s) {
- Log.d(mName, s);
+ Rlog.d(mName, s);
}
/**
@@ -2132,7 +2133,7 @@
* @param s is string log
*/
protected void logd(String s) {
- Log.d(mName, s);
+ Rlog.d(mName, s);
}
/**
@@ -2141,7 +2142,7 @@
* @param s is string log
*/
protected void logv(String s) {
- Log.v(mName, s);
+ Rlog.v(mName, s);
}
/**
@@ -2150,7 +2151,7 @@
* @param s is string log
*/
protected void logi(String s) {
- Log.i(mName, s);
+ Rlog.i(mName, s);
}
/**
@@ -2159,7 +2160,7 @@
* @param s is string log
*/
protected void logw(String s) {
- Log.w(mName, s);
+ Rlog.w(mName, s);
}
/**
@@ -2168,7 +2169,7 @@
* @param s is string log
*/
protected void loge(String s) {
- Log.e(mName, s);
+ Rlog.e(mName, s);
}
/**
@@ -2178,6 +2179,6 @@
* @param e is a Throwable which logs additional information.
*/
protected void loge(String s, Throwable e) {
- Log.e(mName, s, e);
+ Rlog.e(mName, s, e);
}
}
diff --git a/src/com/android/cellbroadcastservice/WakeLockStateMachine.java b/src/com/android/cellbroadcastservice/WakeLockStateMachine.java
index 1874cef..1d08f54 100644
--- a/src/com/android/cellbroadcastservice/WakeLockStateMachine.java
+++ b/src/com/android/cellbroadcastservice/WakeLockStateMachine.java
@@ -16,7 +16,6 @@
package com.android.cellbroadcastservice;
-import android.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -24,7 +23,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemProperties;
-import android.util.Log;
+import android.telephony.Rlog;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -55,7 +54,6 @@
/** Broadcast not required due to geo-fencing check */
static final int EVENT_BROADCAST_NOT_REQUIRED = 4;
- @UnsupportedAppUsage
protected Context mContext;
protected AtomicInteger mReceiverCount = new AtomicInteger(0);
@@ -64,7 +62,6 @@
private static final int WAKE_LOCK_TIMEOUT = 3000;
private final DefaultState mDefaultState = new DefaultState();
- @UnsupportedAppUsage
private final IdleState mIdleState = new IdleState();
private final WaitingState mWaitingState = new WaitingState();
@@ -236,10 +233,9 @@
* Log with debug level.
* @param s the string to log
*/
- @UnsupportedAppUsage
@Override
protected void log(String s) {
- Log.d(getName(), s);
+ Rlog.d(getName(), s);
}
/**
@@ -248,7 +244,7 @@
*/
@Override
protected void loge(String s) {
- Log.e(getName(), s);
+ Rlog.e(getName(), s);
}
/**
@@ -258,6 +254,6 @@
*/
@Override
protected void loge(String s, Throwable e) {
- Log.e(getName(), s, e);
+ Rlog.e(getName(), s, e);
}
}
diff --git a/tests/Android.bp b/tests/Android.bp
index f59dde6..ef9568e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -15,7 +15,7 @@
],
srcs: ["src/**/*.java", ":cellbroadcast-shared-srcs"],
platform_apis: true,
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
certificate: "platform",
instrumentation_for: "CellBroadcastServiceModule",
}
diff --git a/tests/src/com/android/cellbroadcastservice/GsmSmsCbMessageTest.java b/tests/src/com/android/cellbroadcastservice/GsmSmsCbMessageTest.java
deleted file mode 100644
index 7cacfb4..0000000
--- a/tests/src/com/android/cellbroadcastservice/GsmSmsCbMessageTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cellbroadcastservice;
-
-import android.telephony.CbGeoUtils;
-import android.telephony.SmsCbCmasInfo;
-import android.telephony.SmsCbMessage;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.util.Log;
-
-import com.android.cellbroadcastservice.CbGeoUtils.Circle;
-import com.android.cellbroadcastservice.CbGeoUtils.Polygon;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@RunWith(AndroidTestingRunner.class)
-public class GsmSmsCbMessageTest extends CellBroadcastServiceTestBase {
-
- private static final String TAG = "GsmSmsCbMessageTest";
-
- @Test
- @SmallTest
- public void testCreateMessageFromBinary() throws Exception {
- final byte[] pdu = hexStringToBytes("0111130F6A0101C8329BFD06559BD429E8FE96B3C92C101D9D9"
- + "E83D27350B22E1C7EAFF234BDFCADB962AE9A6BCE06A1DCE57B0AD40241C3E73208147B81622E000"
- + "0000000000000000000000000000000000000000000000039EA013028B53640A4BF600063204C8FC"
- + "D063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E"
- + "683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E401"
- + "00C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A"
- + "4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FC"
- + "D063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E"
- + "683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E401"
- + "00C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A"
- + "4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FC"
- + "D063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E"
- + "683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E401"
- + "00C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A"
- + "4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063");
- SmsCbHeader header = new SmsCbHeader(pdu);
-
- byte[][] pdus = new byte[1][];
- pdus[0] = pdu;
-
- SmsCbMessage msg = GsmSmsCbMessage.createSmsCbMessage(mMockedContext, header, null, pdus,
- 0);
-
- Log.d(TAG, "msg=" + msg);
-
- assertEquals(SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE,
- msg.getGeographicalScope());
- assertEquals(3946, msg.getSerialNumber());
- assertEquals(SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED,
- msg.getServiceCategory());
- assertEquals("en", msg.getLanguageCode());
- assertEquals("Hello UMTS world, this is IuBC§Write§5.1.5.sl (new) - Page 1/ 1.",
- msg.getMessageBody());
- assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, msg.getMessagePriority());
-
- SmsCbCmasInfo cmasInfo = msg.getCmasWarningInfo();
- assertEquals(SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, cmasInfo.getMessageClass());
- assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
- assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
- assertEquals(SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE, cmasInfo.getUrgency());
- assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED, cmasInfo.getCertainty());
-
- List<CbGeoUtils.Geometry> geometries = msg.getGeometries();
- for (int i = 0; i < 15; i++) {
- assertEquals(1546.875, ((Circle) geometries.get(i * 2)).getRadius());
- assertEquals(37.41462707519531, ((Circle) geometries.get(i * 2)).getCenter().lat);
- assertEquals(-122.08093643188477, ((Circle) geometries.get(i * 2)).getCenter().lng);
- assertEquals(11.109967231750488,
- ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(0).lat);
- assertEquals(22.219934463500977,
- ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(0).lng);
- assertEquals(33.32998752593994, 44,
- ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(1).lat);
- assertEquals(44.43995475769043,
- ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(1).lng);
- assertEquals(55.549964904785156,
- ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(2).lat);
- assertEquals(-56.560020446777344,
- ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(2).lng);
- }
- }
-
- @Test
- @SmallTest
- public void testCreateTriggerMessage() throws Exception {
- final byte[] pdu = hexStringToBytes("0001113001010010C0111204D2");
- GsmSmsCbMessage.GeoFencingTriggerMessage triggerMessage =
- GsmSmsCbMessage.createGeoFencingTriggerMessage(pdu);
-
- Log.d(TAG, "trigger message=" + triggerMessage);
-
- assertEquals(1, triggerMessage.type);
- assertEquals(1, triggerMessage.cbIdentifiers.size());
- assertEquals(1234, triggerMessage.cbIdentifiers.get(0).serialNumber);
- assertEquals(SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL,
- triggerMessage.cbIdentifiers.get(0).messageIdentifier);
- }
-}
diff --git a/tests/src/com/android/cellbroadcastservice/tests/CdmaSmsMessageTest.java b/tests/src/com/android/cellbroadcastservice/tests/CdmaSmsMessageTest.java
new file mode 100644
index 0000000..7e34a40
--- /dev/null
+++ b/tests/src/com/android/cellbroadcastservice/tests/CdmaSmsMessageTest.java
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cellbroadcastservice.tests;
+
+import android.content.Context;
+import android.hardware.radio.V1_0.CdmaSmsMessage;
+import android.telephony.Rlog;
+import android.telephony.SmsCbCmasInfo;
+import android.telephony.SmsCbMessage;
+import android.telephony.cdma.CdmaSmsCbProgramData;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.cellbroadcastservice.BearerData;
+import com.android.cellbroadcastservice.DefaultCellBroadcastService;
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.cdma.SmsMessage;
+import com.android.internal.telephony.cdma.SmsMessageConverter;
+import com.android.internal.util.BitwiseOutputStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test cases to verify that our parseBroadcastSms function correctly works with the
+ * CdmaSmsMessage class.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CdmaSmsMessageTest extends CellBroadcastServiceTestBase {
+
+ private static final String TAG = "CdmaSmsMessageTest";
+
+ /* Copy of private subparameter identifier constants from BearerData class. */
+ private static final byte SUBPARAM_MESSAGE_IDENTIFIER = (byte) 0x00;
+ private static final byte SUBPARAM_USER_DATA = (byte) 0x01;
+ private static final byte SUBPARAM_PRIORITY_INDICATOR = (byte) 0x08;
+ private static final byte SUBPARAM_LANGUAGE_INDICATOR = (byte) 0x0D;
+ private static final byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12;
+
+ private static final int TELESERVICE_NOT_SET = 0x0000;
+ private static final int TELESERVICE_SCPT = 0x1006;
+
+ /**
+ * Digit Mode Indicator is a 1-bit value that indicates whether
+ * the address digits are 4-bit DTMF codes or 8-bit codes. (See
+ * 3GPP2 C.S0015-B, v2, 3.4.3.3)
+ */
+ private static final int DIGIT_MODE_4BIT_DTMF = 0x00;
+ private static final int DIGIT_MODE_8BIT_CHAR = 0x01;
+
+ /**
+ * Number Mode Indicator is 1-bit value that indicates whether the
+ * address type is a data network address or not. (See 3GPP2
+ * C.S0015-B, v2, 3.4.3.3)
+ */
+ private static final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00;
+ private static final int NUMBER_MODE_DATA_NETWORK = 0x01;
+
+ /**
+ * Number Types for data networks.
+ * (See 3GPP2 C.S005-D, table2.7.1.3.2.4-2 for complete table)
+ * (See 3GPP2 C.S0015-B, v2, 3.4.3.3 for data network subset)
+ * NOTE: value is stored in the parent class ton field.
+ */
+ private static final int TON_UNKNOWN = 0x00;
+
+ /**
+ * Numbering Plan identification is a 0 or 4-bit value that
+ * indicates which numbering plan identification is set. (See
+ * 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3)
+ */
+ private static final int NUMBERING_PLAN_ISDN_TELEPHONY = 0x1;
+
+ /**
+ * User data encoding types.
+ * (See 3GPP2 C.R1001-F, v1.0, table 9.1-1)
+ */
+ public static final int ENCODING_OCTET = 0x00;
+ public static final int ENCODING_IS91_EXTENDED_PROTOCOL = 0x01;
+ public static final int ENCODING_7BIT_ASCII = 0x02;
+ public static final int ENCODING_IA5 = 0x03;
+ public static final int ENCODING_UNICODE_16 = 0x04;
+ public static final int ENCODING_SHIFT_JIS = 0x05;
+ public static final int ENCODING_KOREAN = 0x06;
+ public static final int ENCODING_LATIN_HEBREW = 0x07;
+ public static final int ENCODING_LATIN = 0x08;
+ public static final int ENCODING_GSM_7BIT_ALPHABET = 0x09;
+ public static final int ENCODING_GSM_DCS = 0x0A;
+
+ /**
+ * IS-91 message types.
+ * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3)
+ */
+ public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS = 0x82;
+ public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83;
+ public static final int IS91_MSG_TYPE_CLI = 0x84;
+ public static final int IS91_MSG_TYPE_SHORT_MESSAGE = 0x85;
+
+ /**
+ * Supported message types for CDMA SMS messages
+ * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
+ */
+ public static final int MESSAGE_TYPE_DELIVER = 0x01;
+ public static final int MESSAGE_TYPE_SUBMIT = 0x02;
+ public static final int MESSAGE_TYPE_CANCELLATION = 0x03;
+ public static final int MESSAGE_TYPE_DELIVERY_ACK = 0x04;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ putResources(com.android.cellbroadcastservice.R.bool.config_sms_utf8_support, false);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Initialize a Parcel for an incoming CDMA cell broadcast. The caller will write the
+ * bearer data and then convert it to an SmsMessage.
+ *
+ * @param serviceCategory the CDMA service category
+ * @return the initialized Parcel
+ */
+ private static CdmaSmsMessage createBroadcastParcel(int serviceCategory) {
+ CdmaSmsMessage msg = new CdmaSmsMessage();
+
+ msg.teleserviceId = TELESERVICE_NOT_SET;
+ msg.isServicePresent = true;
+ msg.serviceCategory = serviceCategory;
+
+ // dummy address (RIL may generate a different dummy address for broadcasts)
+ msg.address.digitMode = DIGIT_MODE_4BIT_DTMF;
+ msg.address.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
+ msg.address.numberType = TON_UNKNOWN;
+ msg.address.numberPlan = NUMBERING_PLAN_ISDN_TELEPHONY;
+ msg.subAddress.subaddressType = 0;
+ msg.subAddress.odd = false;
+ return msg;
+ }
+
+ /**
+ * Initialize a BitwiseOutputStream with the CDMA bearer data subparameters except for
+ * user data. The caller will append the user data and add it to the parcel.
+ *
+ * @param messageId the 16-bit message identifier
+ * @param priority message priority
+ * @param language message language code
+ * @return the initialized BitwiseOutputStream
+ */
+ private static BitwiseOutputStream createBearerDataStream(int messageId, int priority,
+ int language) throws BitwiseOutputStream.AccessException {
+ BitwiseOutputStream bos = new BitwiseOutputStream(10);
+ bos.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
+ bos.write(8, 3); // length: 3 bytes
+ bos.write(4, BearerData.MESSAGE_TYPE_DELIVER);
+ bos.write(8, ((messageId >>> 8) & 0xff));
+ bos.write(8, (messageId & 0xff));
+ bos.write(1, 0); // no User Data Header
+ bos.write(3, 0); // reserved
+
+ if (priority != -1) {
+ bos.write(8, SUBPARAM_PRIORITY_INDICATOR);
+ bos.write(8, 1); // length: 1 byte
+ bos.write(2, (priority & 0x03));
+ bos.write(6, 0); // reserved
+ }
+
+ if (language != -1) {
+ bos.write(8, SUBPARAM_LANGUAGE_INDICATOR);
+ bos.write(8, 1); // length: 1 byte
+ bos.write(8, (language & 0xff));
+ }
+
+ return bos;
+ }
+
+ /**
+ * Write the bearer data array to the parcel, then return a new SmsMessage from the parcel.
+ *
+ * @param msg CdmaSmsMessage containing the CDMA SMS headers
+ * @param bearerData the bearer data byte array to append to the parcel
+ * @return the new SmsMessage created from the parcel
+ */
+ private static SmsMessage createMessageFromParcel(CdmaSmsMessage msg, byte[] bearerData) {
+ for (byte b : bearerData) {
+ msg.bearerData.add(b);
+ }
+ SmsMessage message = SmsMessageConverter.newCdmaSmsMessageFromRil(msg);
+ return message;
+ }
+
+ /**
+ * Create a parcel for an incoming CMAS broadcast, then return a new SmsMessage created
+ * from the parcel.
+ *
+ * @param serviceCategory the CDMA service category
+ * @param messageId the 16-bit message identifier
+ * @param priority message priority
+ * @param language message language code
+ * @param body message body
+ * @param cmasCategory CMAS category (or -1 to skip adding CMAS type 1 elements record)
+ * @param responseType CMAS response type
+ * @param severity CMAS severity
+ * @param urgency CMAS urgency
+ * @param certainty CMAS certainty
+ * @return the newly created SmsMessage object
+ */
+ private static SmsMessage createCmasSmsMessage(int serviceCategory, int messageId, int priority,
+ int language, int encoding, String body, int cmasCategory, int responseType,
+ int severity, int urgency, int certainty) throws Exception {
+ BitwiseOutputStream cmasBos = new BitwiseOutputStream(10);
+ cmasBos.write(8, 0); // CMAE protocol version 0
+
+ if (body != null) {
+ cmasBos.write(8, 0); // Type 0 elements (alert text)
+ encodeBody(encoding, body, true, cmasBos);
+ }
+
+ if (cmasCategory != SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN) {
+ cmasBos.write(8, 1); // Type 1 elements
+ cmasBos.write(8, 4); // length: 4 bytes
+ cmasBos.write(8, (cmasCategory & 0xff));
+ cmasBos.write(8, (responseType & 0xff));
+ cmasBos.write(4, (severity & 0x0f));
+ cmasBos.write(4, (urgency & 0x0f));
+ cmasBos.write(4, (certainty & 0x0f));
+ cmasBos.write(4, 0); // pad to octet boundary
+ }
+
+ byte[] cmasUserData = cmasBos.toByteArray();
+
+ CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
+ BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
+
+ bos.write(8, SUBPARAM_USER_DATA);
+ bos.write(8, cmasUserData.length + 2); // add 2 bytes for msg_encoding and num_fields
+ bos.write(5, ENCODING_OCTET);
+ bos.write(8, cmasUserData.length);
+ bos.writeByteArray(cmasUserData.length * 8, cmasUserData);
+ bos.write(3, 0); // pad to byte boundary
+
+ return createMessageFromParcel(msg, bos.toByteArray());
+ }
+
+ /**
+ * Create a parcel for an incoming CDMA cell broadcast, then return a new SmsMessage created
+ * from the parcel.
+ *
+ * @param serviceCategory the CDMA service category
+ * @param messageId the 16-bit message identifier
+ * @param priority message priority
+ * @param language message language code
+ * @param encoding user data encoding method
+ * @param body the message body
+ * @return the newly created SmsMessage object
+ */
+ private static SmsMessage createBroadcastSmsMessage(int serviceCategory, int messageId,
+ int priority, int language, int encoding, String body) throws Exception {
+ CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
+ BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
+
+ bos.write(8, SUBPARAM_USER_DATA);
+ encodeBody(encoding, body, false, bos);
+
+ return createMessageFromParcel(msg, bos.toByteArray());
+ }
+
+ /**
+ * Append the message length, encoding, and body to the BearerData output stream.
+ * This is used for writing the User Data subparameter for non-CMAS broadcasts and for
+ * writing the alert text for CMAS broadcasts.
+ *
+ * @param encoding one of the CDMA UserData encoding values
+ * @param body the message body
+ * @param isCmasRecord true if this is a CMAS type 0 elements record; false for user data
+ * @param bos the BitwiseOutputStream to write to
+ * @throws Exception on any encoding error
+ */
+ private static void encodeBody(int encoding, String body, boolean isCmasRecord,
+ BitwiseOutputStream bos) throws Exception {
+ if (encoding == ENCODING_7BIT_ASCII || encoding == ENCODING_IA5) {
+ int charCount = body.length();
+ int recordBits = (charCount * 7) + 5; // add 5 bits for char set field
+ int recordOctets = (recordBits + 7) / 8; // round up to octet boundary
+ int padBits = (recordOctets * 8) - recordBits;
+
+ if (!isCmasRecord) {
+ recordOctets++; // add 8 bits for num_fields
+ }
+
+ bos.write(8, recordOctets);
+ bos.write(5, (encoding & 0x1f));
+
+ if (!isCmasRecord) {
+ bos.write(8, charCount);
+ }
+
+ for (int i = 0; i < charCount; i++) {
+ bos.write(7, body.charAt(i));
+ }
+
+ bos.write(padBits, 0); // pad to octet boundary
+ } else if (encoding == ENCODING_GSM_7BIT_ALPHABET
+ || encoding == ENCODING_GSM_DCS) {
+ // convert to 7-bit packed encoding with septet count in index 0 of byte array
+ byte[] encodedBody = GsmAlphabet.stringToGsm7BitPacked(body);
+
+ int charCount = encodedBody[0]; // septet count
+ int recordBits = (charCount * 7) + 5; // add 5 bits for char set field
+ int recordOctets = (recordBits + 7) / 8; // round up to octet boundary
+ int padBits = (recordOctets * 8) - recordBits;
+
+ if (!isCmasRecord) {
+ recordOctets++; // add 8 bits for num_fields
+ if (encoding == ENCODING_GSM_DCS) {
+ recordOctets++; // add 8 bits for DCS (message type)
+ }
+ }
+
+ bos.write(8, recordOctets);
+ bos.write(5, (encoding & 0x1f));
+
+ if (!isCmasRecord && encoding == ENCODING_GSM_DCS) {
+ bos.write(8, 0); // GSM DCS: 7 bit default alphabet, no msg class
+ }
+
+ if (!isCmasRecord) {
+ bos.write(8, charCount);
+ }
+ byte[] bodySeptets = Arrays.copyOfRange(encodedBody, 1, encodedBody.length);
+ bos.writeByteArray(charCount * 7, bodySeptets);
+ bos.write(padBits, 0); // pad to octet boundary
+ } else if (encoding == ENCODING_IS91_EXTENDED_PROTOCOL) {
+ // 6 bit packed encoding with 0x20 offset (ASCII 0x20 - 0x60)
+ int charCount = body.length();
+ int recordBits = (charCount * 6) + 21; // add 21 bits for header fields
+ int recordOctets = (recordBits + 7) / 8; // round up to octet boundary
+ int padBits = (recordOctets * 8) - recordBits;
+
+ bos.write(8, recordOctets);
+
+ bos.write(5, (encoding & 0x1f));
+ bos.write(8, IS91_MSG_TYPE_SHORT_MESSAGE);
+ bos.write(8, charCount);
+
+ for (int i = 0; i < charCount; i++) {
+ bos.write(6, ((int) body.charAt(i) - 0x20));
+ }
+
+ bos.write(padBits, 0); // pad to octet boundary
+ } else {
+ byte[] encodedBody;
+ switch (encoding) {
+ case ENCODING_UNICODE_16:
+ encodedBody = body.getBytes("UTF-16BE");
+ break;
+
+ case ENCODING_SHIFT_JIS:
+ encodedBody = body.getBytes("Shift_JIS");
+ break;
+
+ case ENCODING_KOREAN:
+ encodedBody = body.getBytes("KSC5601");
+ break;
+
+ case ENCODING_LATIN_HEBREW:
+ encodedBody = body.getBytes("ISO-8859-8");
+ break;
+
+ case ENCODING_LATIN:
+ default:
+ encodedBody = body.getBytes("ISO-8859-1");
+ break;
+ }
+ int charCount = body.length(); // use actual char count for num fields
+ int recordOctets = encodedBody.length + 1; // add 1 byte for encoding and pad bits
+ if (!isCmasRecord) {
+ recordOctets++; // add 8 bits for num_fields
+ }
+ bos.write(8, recordOctets);
+ bos.write(5, (encoding & 0x1f));
+ if (!isCmasRecord) {
+ bos.write(8, charCount);
+ }
+ bos.writeByteArray(encodedBody.length * 8, encodedBody);
+ bos.write(3, 0); // pad to octet boundary
+ }
+ }
+
+ private static final String TEST_TEXT = "This is a test CDMA cell broadcast message..."
+ + "678901234567890123456789012345678901234567890";
+
+ private static final String PRES_ALERT =
+ "THE PRESIDENT HAS ISSUED AN EMERGENCY ALERT. CHECK LOCAL MEDIA FOR MORE DETAILS";
+
+ private static final String EXTREME_ALERT = "FLASH FLOOD WARNING FOR SOUTH COCONINO COUNTY"
+ + " - NORTH CENTRAL ARIZONA UNTIL 415 PM MST";
+
+ private static final String SEVERE_ALERT = "SEVERE WEATHER WARNING FOR SOMERSET COUNTY"
+ + " - NEW JERSEY UNTIL 415 PM MST";
+
+ private static final String AMBER_ALERT =
+ "AMBER ALERT:Mountain View,CA VEH'07 Blue Honda Civic CA LIC 5ABC123";
+
+ private static final String MONTHLY_TEST_ALERT = "This is a test of the emergency alert system."
+ + " This is only a test. 89012345678901234567890";
+
+ private static final String IS91_TEXT = "IS91 SHORT MSG"; // max length 14 chars
+
+ /**
+ * Verify that the SmsCbMessage has the correct values for CDMA.
+ *
+ * @param cbMessage the message to test
+ */
+ private static void verifyCbValues(SmsCbMessage cbMessage) {
+ assertEquals(SmsCbMessage.MESSAGE_FORMAT_3GPP2, cbMessage.getMessageFormat());
+ assertEquals(SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, cbMessage.getGeographicalScope());
+ assertEquals(false, cbMessage.isEtwsMessage()); // ETWS on CDMA not currently supported
+ }
+
+ private static void doTestNonEmergencyBroadcast(Context context, int encoding)
+ throws Exception {
+ SmsMessage msg = createBroadcastSmsMessage(123, 456, BearerData.PRIORITY_NORMAL,
+ BearerData.LANGUAGE_ENGLISH, encoding, TEST_TEXT);
+
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(context,
+ 0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ verifyCbValues(cbMessage);
+ assertEquals(123, cbMessage.getServiceCategory());
+ assertEquals(456, cbMessage.getSerialNumber());
+ assertEquals(SmsCbMessage.MESSAGE_PRIORITY_NORMAL, cbMessage.getMessagePriority());
+ assertEquals("en", cbMessage.getLanguageCode());
+ assertEquals(TEST_TEXT, cbMessage.getMessageBody());
+ assertEquals(false, cbMessage.isEmergencyMessage());
+ assertEquals(false, cbMessage.isCmasMessage());
+ }
+
+ @Test
+ public void testNonEmergencyBroadcast7bitAscii() throws Exception {
+ doTestNonEmergencyBroadcast(mMockedContext, ENCODING_7BIT_ASCII);
+ }
+
+ @Test
+ public void testNonEmergencyBroadcast7bitGsm() throws Exception {
+ doTestNonEmergencyBroadcast(mMockedContext, ENCODING_GSM_7BIT_ALPHABET);
+ }
+
+ @Test
+ public void testNonEmergencyBroadcast16bitUnicode() throws Exception {
+ doTestNonEmergencyBroadcast(mMockedContext, ENCODING_UNICODE_16);
+ }
+
+ private static void doTestCmasBroadcast(Context context, int serviceCategory, int messageClass,
+ String body) throws Exception {
+ SmsMessage msg = createCmasSmsMessage(
+ serviceCategory, 1234, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
+ ENCODING_7BIT_ASCII, body, -1, -1, -1, -1, -1);
+
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(context,
+ 0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ verifyCbValues(cbMessage);
+ assertEquals(serviceCategory, cbMessage.getServiceCategory());
+ assertEquals(1234, cbMessage.getSerialNumber());
+ assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
+ assertEquals("en", cbMessage.getLanguageCode());
+ assertEquals(body, cbMessage.getMessageBody());
+ assertEquals(true, cbMessage.isEmergencyMessage());
+ assertEquals(true, cbMessage.isCmasMessage());
+ SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
+ assertEquals(messageClass, cmasInfo.getMessageClass());
+ assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
+ assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
+ assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN, cmasInfo.getSeverity());
+ assertEquals(SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, cmasInfo.getUrgency());
+ assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN, cmasInfo.getCertainty());
+ }
+
+ @Test
+ public void testCmasPresidentialAlert() throws Exception {
+ doTestCmasBroadcast(mMockedContext,
+ CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+ SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT, PRES_ALERT);
+ }
+
+ @Test
+ public void testCmasExtremeAlert() throws Exception {
+ doTestCmasBroadcast(mMockedContext, CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, EXTREME_ALERT);
+ }
+
+ @Test
+ public void testCmasSevereAlert() throws Exception {
+ doTestCmasBroadcast(mMockedContext, CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT,
+ SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT, SEVERE_ALERT);
+ }
+
+ @Test
+ public void testCmasAmberAlert() throws Exception {
+ doTestCmasBroadcast(mMockedContext,
+ CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
+ SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY, AMBER_ALERT);
+ }
+
+ @Test
+ public void testCmasTestMessage() throws Exception {
+ doTestCmasBroadcast(mMockedContext, CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE,
+ SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST, MONTHLY_TEST_ALERT);
+ }
+
+ @Test
+ public void testCmasExtremeAlertType1Elements() throws Exception {
+ SmsMessage msg = createCmasSmsMessage(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ 5678, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
+ ENCODING_7BIT_ASCII, EXTREME_ALERT, SmsCbCmasInfo.CMAS_CATEGORY_ENV,
+ SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, SmsCbCmasInfo.CMAS_SEVERITY_SEVERE,
+ SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY);
+
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(mMockedContext,
+ 0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ verifyCbValues(cbMessage);
+ assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ cbMessage.getServiceCategory());
+ assertEquals(5678, cbMessage.getSerialNumber());
+ assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
+ assertEquals("en", cbMessage.getLanguageCode());
+ assertEquals(EXTREME_ALERT, cbMessage.getMessageBody());
+ assertEquals(true, cbMessage.isEmergencyMessage());
+ assertEquals(true, cbMessage.isCmasMessage());
+ SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
+ assertEquals(SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, cmasInfo.getMessageClass());
+ assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_ENV, cmasInfo.getCategory());
+ assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, cmasInfo.getResponseType());
+ assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_SEVERE, cmasInfo.getSeverity());
+ assertEquals(SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, cmasInfo.getUrgency());
+ assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY, cmasInfo.getCertainty());
+ }
+
+ // VZW requirement is to discard message with unsupported charset. Verify that we return null
+ // for this unsupported character set.
+ @Ignore
+ @Test
+ public void testCmasUnsupportedCharSet() throws Exception {
+ SmsMessage msg = createCmasSmsMessage(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ 12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
+ 0x1F, EXTREME_ALERT, -1, -1, -1, -1, -1);
+
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(mMockedContext,
+ 0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ assertNull("expected null for unsupported charset", cbMessage);
+ }
+
+ // VZW requirement is to discard message with unsupported charset. Verify that we return null
+ // for this unsupported character set.
+ @Test
+ public void testCmasUnsupportedCharSet2() throws Exception {
+ SmsMessage msg = createCmasSmsMessage(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ 67890, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
+ ENCODING_KOREAN, EXTREME_ALERT, -1, -1, -1, -1, -1);
+
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(mMockedContext,
+ 0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ assertNull("expected null for unsupported charset", cbMessage);
+ }
+
+ // VZW requirement is to discard message without record type 0. The framework will decode it
+ // and the app will discard it.
+ @Test
+ public void testCmasNoRecordType0() throws Exception {
+ SmsMessage msg = createCmasSmsMessage(
+ CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT, 1234,
+ BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
+ ENCODING_7BIT_ASCII, null, -1, -1, -1, -1, -1);
+
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(mMockedContext,
+ 0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ verifyCbValues(cbMessage);
+ assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+ cbMessage.getServiceCategory());
+ assertEquals(1234, cbMessage.getSerialNumber());
+ assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
+ assertEquals("en", cbMessage.getLanguageCode());
+ assertEquals(null, cbMessage.getMessageBody());
+ assertEquals(true, cbMessage.isEmergencyMessage());
+ assertEquals(true, cbMessage.isCmasMessage());
+ SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
+ assertEquals(SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT, cmasInfo.getMessageClass());
+ assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
+ assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
+ assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN, cmasInfo.getSeverity());
+ assertEquals(SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, cmasInfo.getUrgency());
+ assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN, cmasInfo.getCertainty());
+ }
+
+ // Make sure we don't throw an exception if we feed completely random data to BearerStream.
+ @Test
+ public void testRandomBearerStreamData() {
+ Random r = new Random(54321);
+ for (int run = 0; run < 1000; run++) {
+ int len = r.nextInt(140);
+ byte[] data = new byte[len];
+ for (int i = 0; i < len; i++) {
+ data[i] = (byte) r.nextInt(256);
+ }
+ // Log.d(TAG, "trying random bearer data run " + run + " length " + len);
+ try {
+ int category = 0x0ff0 + r.nextInt(32); // half CMAS, half non-CMAS
+ CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
+ SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, data);
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(
+ mMockedContext,
+ 0, "", msg.getEnvelopeBearerData(),
+ msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ // with random input, cbMessage will almost always be null (log when it isn't)
+ if (cbMessage != null) {
+ Rlog.d(TAG, "success: " + cbMessage);
+ }
+ } catch (Exception e) {
+ Rlog.d(TAG, "exception thrown", e);
+ fail("Exception in decoder at run " + run + " length " + len + ": " + e);
+ }
+ }
+ }
+
+ // Make sure we don't throw an exception if we put random data in the UserData subparam.
+ @Test
+ public void testRandomUserData() {
+ Random r = new Random(94040);
+ for (int run = 0; run < 1000; run++) {
+ int category = 0x0ff0 + r.nextInt(32); // half CMAS, half non-CMAS
+ CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
+ int len = r.nextInt(140);
+ // Log.d(TAG, "trying random user data run " + run + " length " + len);
+
+ try {
+ BitwiseOutputStream bos = createBearerDataStream(r.nextInt(65536), r.nextInt(4),
+ r.nextInt(256));
+
+ bos.write(8, SUBPARAM_USER_DATA);
+ bos.write(8, len);
+
+ for (int i = 0; i < len; i++) {
+ bos.write(8, r.nextInt(256));
+ }
+
+ SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(mMockedContext, 0, "",
+ msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ } catch (Exception e) {
+ Rlog.d(TAG, "exception thrown", e);
+ fail("Exception in decoder at run " + run + " length " + len + ": " + e);
+ }
+ }
+ }
+
+ /**
+ * Initialize a Parcel for incoming Service Category Program Data teleservice. The caller will
+ * write the bearer data and then convert it to an SmsMessage.
+ *
+ * @return the initialized Parcel
+ */
+ private static CdmaSmsMessage createServiceCategoryProgramDataParcel() {
+ CdmaSmsMessage msg = new CdmaSmsMessage();
+
+ msg.teleserviceId = TELESERVICE_SCPT;
+ msg.isServicePresent = false;
+ msg.serviceCategory = 0;
+
+ // dummy address (RIL may generate a different dummy address for broadcasts)
+ msg.address.digitMode = DIGIT_MODE_4BIT_DTMF;
+ msg.address.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
+ msg.address.numberType = TON_UNKNOWN;
+ msg.address.numberPlan = NUMBERING_PLAN_ISDN_TELEPHONY;
+ msg.subAddress.subaddressType = 0;
+ msg.subAddress.odd = false;
+ return msg;
+ }
+
+ private static final String CAT_EXTREME_THREAT = "Extreme Threat to Life and Property";
+ private static final String CAT_SEVERE_THREAT = "Severe Threat to Life and Property";
+ private static final String CAT_AMBER_ALERTS = "AMBER Alerts";
+
+ @Test
+ public void testServiceCategoryProgramDataAddCategory() throws Exception {
+ CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
+ BitwiseOutputStream bos = createBearerDataStream(123, -1, -1);
+
+ int categoryNameLength = CAT_EXTREME_THREAT.length();
+ int subparamLengthBits = (53 + (categoryNameLength * 7));
+ int subparamLengthBytes = (subparamLengthBits + 7) / 8;
+ int subparamPadBits = (subparamLengthBytes * 8) - subparamLengthBits;
+
+ bos.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA);
+ bos.write(8, subparamLengthBytes);
+ bos.write(5, ENCODING_7BIT_ASCII);
+
+ bos.write(4, CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY);
+ bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT >>> 8));
+ bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT & 0xff));
+ bos.write(8, BearerData.LANGUAGE_ENGLISH);
+ bos.write(8, 100); // max messages
+ bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_DEFAULT_ALERT);
+
+ bos.write(8, categoryNameLength);
+ for (int i = 0; i < categoryNameLength; i++) {
+ bos.write(7, CAT_EXTREME_THREAT.charAt(i));
+ }
+ bos.write(subparamPadBits, 0);
+
+ SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
+ assertNotNull(msg);
+ msg.parseSms();
+ List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
+ assertNotNull(programDataList);
+ assertEquals(1, programDataList.size());
+ CdmaSmsCbProgramData programData = programDataList.get(0);
+ assertEquals(CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY, programData.getOperation());
+ assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ programData.getCategory());
+ assertEquals(CAT_EXTREME_THREAT, programData.getCategoryName());
+ assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
+ assertEquals(100, programData.getMaxMessages());
+ assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_DEFAULT_ALERT, programData.getAlertOption());
+ }
+
+ @Test
+ public void testServiceCategoryProgramDataDeleteTwoCategories() throws Exception {
+ CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
+ BitwiseOutputStream bos = createBearerDataStream(456, -1, -1);
+
+ int category1NameLength = CAT_SEVERE_THREAT.length();
+ int category2NameLength = CAT_AMBER_ALERTS.length();
+
+ int subparamLengthBits = (101 + (category1NameLength * 7) + (category2NameLength * 7));
+ int subparamLengthBytes = (subparamLengthBits + 7) / 8;
+ int subparamPadBits = (subparamLengthBytes * 8) - subparamLengthBits;
+
+ bos.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA);
+ bos.write(8, subparamLengthBytes);
+ bos.write(5, ENCODING_7BIT_ASCII);
+
+ bos.write(4, CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY);
+ bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT >>> 8));
+ bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT & 0xff));
+ bos.write(8, BearerData.LANGUAGE_ENGLISH);
+ bos.write(8, 0); // max messages
+ bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT);
+
+ bos.write(8, category1NameLength);
+ for (int i = 0; i < category1NameLength; i++) {
+ bos.write(7, CAT_SEVERE_THREAT.charAt(i));
+ }
+
+ bos.write(4, CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY);
+ bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY >>> 8));
+ bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY & 0xff));
+ bos.write(8, BearerData.LANGUAGE_ENGLISH);
+ bos.write(8, 0); // max messages
+ bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT);
+
+ bos.write(8, category2NameLength);
+ for (int i = 0; i < category2NameLength; i++) {
+ bos.write(7, CAT_AMBER_ALERTS.charAt(i));
+ }
+
+ bos.write(subparamPadBits, 0);
+
+ SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
+ assertNotNull(msg);
+ msg.parseSms();
+ List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
+ assertNotNull(programDataList);
+ assertEquals(2, programDataList.size());
+
+ CdmaSmsCbProgramData programData = programDataList.get(0);
+ assertEquals(CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY, programData.getOperation());
+ assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT,
+ programData.getCategory());
+ assertEquals(CAT_SEVERE_THREAT, programData.getCategoryName());
+ assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
+ assertEquals(0, programData.getMaxMessages());
+ assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT, programData.getAlertOption());
+
+ programData = programDataList.get(1);
+ assertEquals(CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY, programData.getOperation());
+ assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
+ programData.getCategory());
+ assertEquals(CAT_AMBER_ALERTS, programData.getCategoryName());
+ assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
+ assertEquals(0, programData.getMaxMessages());
+ assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT, programData.getAlertOption());
+ }
+
+ private static final byte[] CMAS_TEST_BEARER_DATA = {
+ 0x00, 0x03, 0x1C, 0x78, 0x00, 0x01, 0x59, 0x02, (byte) 0xB8, 0x00, 0x02, 0x10,
+ (byte) 0xAA,
+ 0x68, (byte) 0xD3, (byte) 0xCD, 0x06, (byte) 0x9E, 0x68, 0x30, (byte) 0xA0, (byte) 0xE9,
+ (byte) 0x97, (byte) 0x9F, 0x44, 0x1B, (byte) 0xF3, 0x20, (byte) 0xE9, (byte) 0xA3,
+ 0x2A, 0x08, 0x7B, (byte) 0xF6, (byte) 0xED, (byte) 0xCB, (byte) 0xCB, 0x1E, (byte) 0x9C,
+ 0x3B, 0x10, 0x4D, (byte) 0xDF, (byte) 0x8B, 0x4E,
+ (byte) 0xCC, (byte) 0xA8, 0x20, (byte) 0xEC, (byte) 0xCB, (byte) 0xCB, (byte) 0xA2,
+ 0x0A,
+ 0x7E, 0x79, (byte) 0xF4, (byte) 0xCB, (byte) 0xB5, 0x72, 0x0A, (byte) 0x9A, 0x34,
+ (byte) 0xF3, 0x41, (byte) 0xA7, (byte) 0x9A, 0x0D, (byte) 0xFB, (byte) 0xB6, 0x79, 0x41,
+ (byte) 0x85, 0x07, 0x4C, (byte) 0xBC, (byte) 0xFA, 0x2E, 0x00, 0x08, 0x20, 0x58, 0x38,
+ (byte) 0x88, (byte) 0x80, 0x10, 0x54, 0x06, 0x38, 0x20, 0x60,
+ 0x30, (byte) 0xA8, (byte) 0x81, (byte) 0x90, 0x20, 0x08
+ };
+
+ // Test case for CMAS test message received on the Sprint network.
+ @Test
+ public void testDecodeRawBearerData() {
+ CdmaSmsMessage cdmaSmsMessage =
+ createBroadcastParcel(CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE);
+ SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, CMAS_TEST_BEARER_DATA);
+
+ SmsCbMessage cbMessage =
+ DefaultCellBroadcastService.parseBroadcastSms(mMockedContext,
+ 0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
+ //SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
+ assertNotNull("expected non-null for bearer data", cbMessage);
+ assertEquals("geoScope", cbMessage.getGeographicalScope(), 1);
+ assertEquals("serialNumber", cbMessage.getSerialNumber(), 51072);
+ assertEquals("serviceCategory", cbMessage.getServiceCategory(),
+ CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE);
+ assertEquals("payload", cbMessage.getMessageBody(),
+ "This is a test of the Commercial Mobile Alert System. This is only a test.");
+
+ SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
+ assertNotNull("expected non-null for CMAS info", cmasInfo);
+ assertEquals("category", cmasInfo.getCategory(), SmsCbCmasInfo.CMAS_CATEGORY_OTHER);
+ assertEquals("responseType", cmasInfo.getResponseType(),
+ SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE);
+ assertEquals("severity", cmasInfo.getSeverity(), SmsCbCmasInfo.CMAS_SEVERITY_SEVERE);
+ assertEquals("urgency", cmasInfo.getUrgency(), SmsCbCmasInfo.CMAS_URGENCY_EXPECTED);
+ assertEquals("certainty", cmasInfo.getCertainty(), SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY);
+ }
+}
diff --git a/tests/src/com/android/cellbroadcastservice/CellBroadcastHandlerTest.java b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastHandlerTest.java
similarity index 88%
rename from tests/src/com/android/cellbroadcastservice/CellBroadcastHandlerTest.java
rename to tests/src/com/android/cellbroadcastservice/tests/CellBroadcastHandlerTest.java
index 766ea03..3fce3c5 100644
--- a/tests/src/com/android/cellbroadcastservice/CellBroadcastHandlerTest.java
+++ b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastHandlerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cellbroadcastservice;
+package com.android.cellbroadcastservice.tests;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -35,6 +35,10 @@
import android.testing.TestableLooper;
import android.text.format.DateUtils;
+import com.android.cellbroadcastservice.CellBroadcastHandler;
+import com.android.cellbroadcastservice.CellBroadcastProvider;
+import com.android.cellbroadcastservice.SmsCbConstants;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -57,14 +61,15 @@
private class CellBroadcastContentProvider extends MockContentProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ String sortOrder) {
if (uri.compareTo(Telephony.CellBroadcasts.CONTENT_URI) == 0) {
- MatrixCursor mc = new MatrixCursor(Telephony.CellBroadcasts.QUERY_COLUMNS_FWK);
+ MatrixCursor mc = new MatrixCursor(CellBroadcastProvider.QUERY_COLUMNS);
mc.addRow(new Object[]{
1, // _ID
0, // SLOT_INDEX
+ 1, // SUB_ID
0, // GEOGRAPHICAL_SCOPE
"311480", // PLMN
0, // LAC
@@ -115,7 +120,8 @@
doReturn(mMockedResources).when(mMockedResourcesCache).get(anyInt());
replaceInstance(CellBroadcastHandler.class, "mResourcesCache", mCellBroadcastHandler,
mMockedResourcesCache);
- putResources(R.integer.message_expiration_time, (int) DateUtils.DAY_IN_MILLIS);
+ putResources(com.android.cellbroadcastservice.R.integer.message_expiration_time,
+ (int) DateUtils.DAY_IN_MILLIS);
}
@After
@@ -124,11 +130,11 @@
}
private SmsCbMessage createSmsCbMessage(int serialNumber, int serviceCategory,
- String messageBody) {
+ String messageBody) {
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
0, serialNumber, new SmsCbLocation(),
serviceCategory, "en", messageBody, 3,
- null, null, 0);
+ null, null, 0, 1);
}
@Test
@@ -155,7 +161,7 @@
@Test
@SmallTest
public void testNotDuplicateMessageBodyDifferent() throws Exception {
- putResources(R.bool.duplicate_compare_body, true);
+ putResources(com.android.cellbroadcastservice.R.bool.duplicate_compare_body, true);
SmsCbMessage msg = createSmsCbMessage(1234, 4370, "msg");
assertFalse(mCellBroadcastHandler.isDuplicate(msg));
}
diff --git a/tests/src/com/android/cellbroadcastservice/CellBroadcastProviderTest.java b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastProviderTest.java
similarity index 93%
rename from tests/src/com/android/cellbroadcastservice/CellBroadcastProviderTest.java
rename to tests/src/com/android/cellbroadcastservice/tests/CellBroadcastProviderTest.java
index dc9837f..b19d55f 100644
--- a/tests/src/com/android/cellbroadcastservice/CellBroadcastProviderTest.java
+++ b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastProviderTest.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package com.android.cellbroadcastservice;
+package com.android.cellbroadcastservice.tests;
+
+import static com.android.cellbroadcastservice.CellBroadcastProvider.QUERY_COLUMNS;
import static com.google.common.truth.Truth.assertThat;
@@ -25,10 +27,11 @@
import android.database.Cursor;
import android.net.Uri;
import android.provider.Telephony.CellBroadcasts;
+import android.telephony.Rlog;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
-import android.util.Log;
+import com.android.cellbroadcastservice.CellBroadcastProvider;
import com.android.cellbroadcastservice.CellBroadcastProvider.PermissionChecker;
import junit.framework.TestCase;
@@ -65,30 +68,6 @@
private static final String SELECT_BY_ID = CellBroadcasts._ID + "=?";
- private static final String[] QUERY_COLUMNS = {
- CellBroadcasts._ID,
- CellBroadcasts.GEOGRAPHICAL_SCOPE,
- CellBroadcasts.PLMN,
- CellBroadcasts.LAC,
- CellBroadcasts.CID,
- CellBroadcasts.SERIAL_NUMBER,
- CellBroadcasts.SERVICE_CATEGORY,
- CellBroadcasts.LANGUAGE_CODE,
- CellBroadcasts.MESSAGE_BODY,
- CellBroadcasts.MESSAGE_FORMAT,
- CellBroadcasts.MESSAGE_PRIORITY,
- CellBroadcasts.ETWS_WARNING_TYPE,
- CellBroadcasts.CMAS_MESSAGE_CLASS,
- CellBroadcasts.CMAS_CATEGORY,
- CellBroadcasts.CMAS_RESPONSE_TYPE,
- CellBroadcasts.CMAS_SEVERITY,
- CellBroadcasts.CMAS_URGENCY,
- CellBroadcasts.CMAS_CERTAINTY,
- CellBroadcasts.RECEIVED_TIME,
- CellBroadcasts.MESSAGE_BROADCASTED,
- CellBroadcasts.GEOMETRIES
- };
-
private CellBroadcastProviderTestable mCellBroadcastProviderTestable;
private MockContextWithProvider mContext;
private MockContentResolver mContentResolver;
@@ -346,7 +325,7 @@
@Override
public Object getSystemService(String name) {
- Log.d(TAG, "getSystemService: returning null");
+ Rlog.d(TAG, "getSystemService: returning null");
return null;
}
diff --git a/tests/src/com/android/cellbroadcastservice/CellBroadcastProviderTestable.java b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastProviderTestable.java
similarity index 90%
rename from tests/src/com/android/cellbroadcastservice/CellBroadcastProviderTestable.java
rename to tests/src/com/android/cellbroadcastservice/tests/CellBroadcastProviderTestable.java
index 2be7512..06fde6b 100644
--- a/tests/src/com/android/cellbroadcastservice/CellBroadcastProviderTestable.java
+++ b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastProviderTestable.java
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-package com.android.cellbroadcastservice;
+package com.android.cellbroadcastservice.tests;
import android.content.Context;
import android.content.pm.ProviderInfo;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
-import android.util.Log;
+import android.telephony.Rlog;
import androidx.test.InstrumentationRegistry;
+import com.android.cellbroadcastservice.CellBroadcastProvider;
+
public class CellBroadcastProviderTestable extends CellBroadcastProvider {
private static final String TAG = CellBroadcastProviderTestable.class.getSimpleName();
@@ -34,7 +36,7 @@
@Override
public boolean onCreate() {
// DO NOT call super.onCreate(), otherwise the permission checker will be override.
- Log.d(TAG, "CellBroadcastProviderTestable onCreate");
+ Rlog.d(TAG, "CellBroadcastProviderTestable onCreate");
mDbHelper = new InMemoryCellBroadcastProviderDbHelper();
return true;
}
@@ -53,7 +55,7 @@
@Override
public void onCreate(SQLiteDatabase db) {
- Log.d(TAG, "IN MEMORY DB CREATED");
+ Rlog.d(TAG, "IN MEMORY DB CREATED");
db.execSQL(getStringForCellBroadcastTableCreation(CELL_BROADCASTS_TABLE_NAME));
}
diff --git a/tests/src/com/android/cellbroadcastservice/CellBroadcastServiceTestBase.java b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastServiceTestBase.java
similarity index 97%
rename from tests/src/com/android/cellbroadcastservice/CellBroadcastServiceTestBase.java
rename to tests/src/com/android/cellbroadcastservice/tests/CellBroadcastServiceTestBase.java
index 74659dd..c230f2d 100644
--- a/tests/src/com/android/cellbroadcastservice/CellBroadcastServiceTestBase.java
+++ b/tests/src/com/android/cellbroadcastservice/tests/CellBroadcastServiceTestBase.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.cellbroadcastservice;
+package com.android.cellbroadcastservice.tests;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -135,6 +136,7 @@
return null;
}).when(mMockedContext).registerReceiver(
any(BroadcastReceiver.class), any(IntentFilter.class));
+ doReturn(true).when(mMockedLocationManager).isProviderEnabled(anyString());
}
protected void tearDown() throws Exception {
diff --git a/tests/src/com/android/cellbroadcastservice/GsmCellBroadcastHandlerTest.java b/tests/src/com/android/cellbroadcastservice/tests/GsmCellBroadcastHandlerTest.java
similarity index 76%
rename from tests/src/com/android/cellbroadcastservice/GsmCellBroadcastHandlerTest.java
rename to tests/src/com/android/cellbroadcastservice/tests/GsmCellBroadcastHandlerTest.java
index 1ff4604..1fefbbf 100644
--- a/tests/src/com/android/cellbroadcastservice/GsmCellBroadcastHandlerTest.java
+++ b/tests/src/com/android/cellbroadcastservice/tests/GsmCellBroadcastHandlerTest.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.cellbroadcastservice;
+package com.android.cellbroadcastservice.tests;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.ContentValues;
@@ -28,12 +29,13 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.location.Location;
+import android.location.LocationRequest;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.provider.Telephony;
import android.telephony.SmsCbCmasInfo;
-import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
@@ -42,14 +44,21 @@
import android.testing.TestableLooper;
import android.text.format.DateUtils;
+import com.android.cellbroadcastservice.CellBroadcastHandler;
+import com.android.cellbroadcastservice.CellBroadcastProvider;
+import com.android.cellbroadcastservice.GsmCellBroadcastHandler;
+import com.android.cellbroadcastservice.SmsCbConstants;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.Map;
+import java.util.function.Consumer;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -72,11 +81,12 @@
if (uri.compareTo(Telephony.CellBroadcasts.CONTENT_URI) == 0
&& Long.parseLong(selectionArgs[selectionArgs.length - 1]) <= receivedTime) {
- MatrixCursor mc = new MatrixCursor(Telephony.CellBroadcasts.QUERY_COLUMNS_FWK);
+ MatrixCursor mc = new MatrixCursor(CellBroadcastProvider.QUERY_COLUMNS);
mc.addRow(new Object[]{
1, // _ID
0, // SLOT_INDEX
+ 1, // SUB_ID
0, // GEOGRAPHICAL_SCOPE
"311480", // PLMN
0, // LAC
@@ -149,8 +159,9 @@
doReturn(mMockedResources).when(mMockedResourcesCache).get(anyInt());
replaceInstance(CellBroadcastHandler.class, "mResourcesCache",
mGsmCellBroadcastHandler, mMockedResourcesCache);
- putResources(R.integer.message_expiration_time, 86400000);
- putResources(com.android.internal.R.array.config_defaultCellBroadcastReceiverPkgs,
+ putResources(com.android.cellbroadcastservice.R.integer.message_expiration_time, 86400000);
+ putResources(
+ com.android.cellbroadcastservice.R.array.config_defaultCellBroadcastReceiverPkgs,
new String[]{"fake.cellbroadcast.pkg"});
}
@@ -159,17 +170,10 @@
super.tearDown();
}
- private SmsCbMessage createSmsCbMessage(int serialNumber, int serviceCategory,
- String messageBody) {
- return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
- 0, serialNumber, new SmsCbLocation(),
- serviceCategory, "en", messageBody, 3,
- null, null, 0);
- }
-
@Test
@SmallTest
public void testTriggerMessage() throws Exception {
+ doReturn(false).when(mMockedLocationManager).isProviderEnabled(anyString());
final byte[] pdu = hexStringToBytes("0001113001010010C0111204D2");
mGsmCellBroadcastHandler.onGsmCellBroadcastSms(0, pdu);
mTestableLooper.processAllMessages();
@@ -190,7 +194,8 @@
@Test
@SmallTest
public void testAirplaneModeReset() {
- putResources(R.bool.reset_on_power_cycle_or_airplane_mode, true);
+ putResources(com.android.cellbroadcastservice.R.bool.reset_on_power_cycle_or_airplane_mode,
+ true);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", true);
// Send fake airplane mode on event.
@@ -203,4 +208,27 @@
verify(mMockedContext, never()).sendOrderedBroadcast(any(), anyString(), anyString(),
any(), any(), anyInt(), any(), any());
}
+
+ @Test
+ @SmallTest
+ public void testGeofencingAlertOutOfPolygon() {
+ final byte[] pdu = hexStringToBytes("01111D7090010254747A0E4ACF416110B538A582DE6650906AA28"
+ + "2AE6979995D9ECF41C576597E2EBBC77950905D96D3D3EE33689A9FD3CB6D1708CA2E87E76550FAE"
+ + "C7ECBCB203ABA0C6A97E7F3F0B9EC02C15CB5769A5D0652A030FB1ECECF5D5076393C2F83C8E9B9B"
+ + "C7C0ECBC9203A3A3D07B5CBF379F85C06E16030580D660BB662B51A0D57CC3500000000000000000"
+ + "0000000000000000000000000000000000000000000000000003021002078B53B6CA4B84B53988A4"
+ + "B86B53958A4C2DB53B54A4C28B53B6CA4B840100CFF");
+ mGsmCellBroadcastHandler.onGsmCellBroadcastSms(0, pdu);
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<Consumer<Location>> captor = ArgumentCaptor.forClass(Consumer.class);
+ verify(mMockedLocationManager, times(2)).getCurrentLocation(
+ any(LocationRequest.class), any(), any(), captor.capture());
+
+ Consumer<Location> consumer = captor.getValue();
+ consumer.accept(Mockito.mock(Location.class));
+
+ verify(mMockedContext, never()).sendOrderedBroadcast(any(), anyString(), anyString(),
+ any(), any(), anyInt(), any(), any());
+ }
}
diff --git a/tests/src/com/android/cellbroadcastservice/tests/GsmSmsCbMessageTest.java b/tests/src/com/android/cellbroadcastservice/tests/GsmSmsCbMessageTest.java
new file mode 100644
index 0000000..1547a56
--- /dev/null
+++ b/tests/src/com/android/cellbroadcastservice/tests/GsmSmsCbMessageTest.java
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cellbroadcastservice.tests;
+
+import android.telephony.CbGeoUtils;
+import android.telephony.Rlog;
+import android.telephony.SmsCbCmasInfo;
+import android.telephony.SmsCbEtwsInfo;
+import android.telephony.SmsCbLocation;
+import android.telephony.SmsCbMessage;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.cellbroadcastservice.CbGeoUtils.Circle;
+import com.android.cellbroadcastservice.CbGeoUtils.Polygon;
+import com.android.cellbroadcastservice.GsmSmsCbMessage;
+import com.android.cellbroadcastservice.R;
+import com.android.cellbroadcastservice.SmsCbConstants;
+import com.android.cellbroadcastservice.SmsCbHeader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Random;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class GsmSmsCbMessageTest extends CellBroadcastServiceTestBase {
+
+ private static final String TAG = "GsmSmsCbMessageTest";
+
+ private static final SmsCbLocation TEST_LOCATION = new SmsCbLocation("94040", 1234, 5678);
+
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testGetEtwsPrimaryMessage() {
+ String testMessage1 = "Testmessage1";
+ String testMessage2 = "Testmessage2";
+ String testMessage3 = "Testmessage3";
+ String testMessage4 = "Testmessage4";
+ String testMessage5 = "Testmessage5";
+
+ putResources(R.string.etws_primary_default_message_earthquake, testMessage1);
+ putResources(R.string.etws_primary_default_message_tsunami, testMessage2);
+ putResources(R.string.etws_primary_default_message_earthquake_and_tsunami, testMessage3);
+ putResources(R.string.etws_primary_default_message_test, testMessage4);
+ putResources(R.string.etws_primary_default_message_others, testMessage5);
+
+ String message = GsmSmsCbMessage.getEtwsPrimaryMessage(mMockedContext,
+ SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE);
+ Rlog.d("GsmSmsCbMessageTest", "earthquake message=" + message);
+ assertEquals(testMessage1, message);
+
+ message = GsmSmsCbMessage.getEtwsPrimaryMessage(mMockedContext,
+ SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI);
+ Rlog.d("GsmSmsCbMessageTest", "tsunami message=" + message);
+ assertEquals(testMessage2, message);
+
+ message = GsmSmsCbMessage.getEtwsPrimaryMessage(mMockedContext,
+ SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI);
+ Rlog.d("GsmSmsCbMessageTest", "earthquake and tsunami message=" + message);
+ assertEquals(testMessage3, message);
+
+ message = GsmSmsCbMessage.getEtwsPrimaryMessage(mMockedContext,
+ SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE);
+ Rlog.d("GsmSmsCbMessageTest", "test message=" + message);
+ assertEquals(testMessage4, message);
+
+ message = GsmSmsCbMessage.getEtwsPrimaryMessage(mMockedContext,
+ SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY);
+ Rlog.d("GsmSmsCbMessageTest", "others message=" + message);
+ assertEquals(testMessage5, message);
+ }
+
+ @Test
+ public void testCreateMessageFromBinary() throws Exception {
+ final byte[] pdu = hexStringToBytes("0111130F6A0101C8329BFD06559BD429E8FE96B3C92C101D9D9"
+ + "E83D27350B22E1C7EAFF234BDFCADB962AE9A6BCE06A1DCE57B0AD40241C3E73208147B81622E000"
+ + "0000000000000000000000000000000000000000000000039EA013028B53640A4BF600063204C8FC"
+ + "D063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E"
+ + "683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E401"
+ + "00C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A"
+ + "4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FC"
+ + "D063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E"
+ + "683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E401"
+ + "00C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A"
+ + "4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FC"
+ + "D063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E"
+ + "683CF01215F1E40100C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E401"
+ + "00C053028B53640A4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A"
+ + "4BF600063204C8FCD063F341AF67167E683CF01215F1E40100C053028B53640A4BF600063");
+ SmsCbHeader header = new SmsCbHeader(pdu);
+
+ byte[][] pdus = new byte[1][];
+ pdus[0] = pdu;
+
+ SmsCbMessage msg = GsmSmsCbMessage.createSmsCbMessage(mMockedContext, header, null, pdus,
+ 0);
+
+ Rlog.d(TAG, "msg=" + msg);
+
+ assertEquals(SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE,
+ msg.getGeographicalScope());
+ assertEquals(3946, msg.getSerialNumber());
+ assertEquals(SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED,
+ msg.getServiceCategory());
+ assertEquals("en", msg.getLanguageCode());
+ assertEquals("Hello UMTS world, this is IuBC§Write§5.1.5.sl (new) - Page 1/ 1.",
+ msg.getMessageBody());
+ assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, msg.getMessagePriority());
+
+ SmsCbCmasInfo cmasInfo = msg.getCmasWarningInfo();
+ assertEquals(SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, cmasInfo.getMessageClass());
+ assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
+ assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
+ assertEquals(SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE, cmasInfo.getUrgency());
+ assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED, cmasInfo.getCertainty());
+
+ List<CbGeoUtils.Geometry> geometries = msg.getGeometries();
+ for (int i = 0; i < 15; i++) {
+ assertEquals(1546.875, ((Circle) geometries.get(i * 2)).getRadius());
+ assertEquals(37.41462707519531, ((Circle) geometries.get(i * 2)).getCenter().lat);
+ assertEquals(-122.08093643188477, ((Circle) geometries.get(i * 2)).getCenter().lng);
+ assertEquals(11.109967231750488,
+ ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(0).lat);
+ assertEquals(22.219934463500977,
+ ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(0).lng);
+ assertEquals(33.32998752593994, 44,
+ ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(1).lat);
+ assertEquals(44.43995475769043,
+ ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(1).lng);
+ assertEquals(55.549964904785156,
+ ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(2).lat);
+ assertEquals(-56.560020446777344,
+ ((Polygon) geometries.get(i * 2 + 1)).getVertices().get(2).lng);
+ }
+ }
+
+ @Test
+ public void testCreateTriggerMessage() throws Exception {
+ final byte[] pdu = hexStringToBytes("0001113001010010C0111204D2");
+ GsmSmsCbMessage.GeoFencingTriggerMessage triggerMessage =
+ GsmSmsCbMessage.createGeoFencingTriggerMessage(pdu);
+
+ Rlog.d(TAG, "trigger message=" + triggerMessage);
+
+ assertEquals(1, triggerMessage.type);
+ assertEquals(1, triggerMessage.cbIdentifiers.size());
+ assertEquals(1234, triggerMessage.cbIdentifiers.get(0).serialNumber);
+ assertEquals(SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL,
+ triggerMessage.cbIdentifiers.get(0).messageIdentifier);
+ }
+
+ private SmsCbMessage createFromPdu(byte[] pdu) {
+ try {
+ SmsCbHeader header = new SmsCbHeader(pdu);
+ byte[][] pdus = new byte[1][];
+ pdus[0] = pdu;
+ return GsmSmsCbMessage.createSmsCbMessage(InstrumentationRegistry.getContext(), header,
+ TEST_LOCATION, pdus, /* slotIndex */ 0);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ private void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) {
+ pdu[0] = b;
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected geographical scope decoded", expectedGs, msg
+ .getGeographicalScope());
+ }
+
+ @Test
+ public void testCreateNullPdu() {
+ SmsCbMessage msg = createFromPdu(null);
+ assertNull("createFromPdu(byte[] with null pdu should return null", msg);
+ }
+
+ @Test
+ public void testCreateTooShortPdu() {
+ byte[] pdu = new byte[4];
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertNull("createFromPdu(byte[] with too short pdu should return null", msg);
+ }
+
+ @Test
+ public void testGetGeographicalScope() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x40, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91, (byte) 0xCB,
+ (byte) 0xE6,
+ (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07, (byte) 0x85, (byte) 0xD9,
+ (byte) 0x70,
+ (byte) 0x74, (byte) 0x58, (byte) 0x5C, (byte) 0xA6, (byte) 0x83, (byte) 0xDA,
+ (byte) 0xE5,
+ (byte) 0xF9, (byte) 0x3C, (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE,
+ (byte) 0x69,
+ (byte) 0x3A, (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5,
+ (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75,
+ (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93, (byte) 0xC9,
+ (byte) 0x69,
+ (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00
+ };
+
+ doTestGeographicalScopeValue(pdu, (byte) 0x00,
+ SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE);
+ doTestGeographicalScopeValue(pdu, (byte) 0x40, SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte) 0x80,
+ SmsCbMessage.GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte) 0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
+ }
+
+ @Test
+ public void testGetGeographicalScopeUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x32, (byte) 0xC0, (byte) 0x00, (byte) 0x40,
+
+ (byte) 0x01,
+
+ (byte) 0x41, (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91,
+ (byte) 0xCB, (byte) 0xE6, (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07,
+ (byte) 0x85, (byte) 0xD9, (byte) 0x70, (byte) 0x74, (byte) 0x58, (byte) 0x5C,
+ (byte) 0xA6, (byte) 0x83, (byte) 0xDA, (byte) 0xE5, (byte) 0xF9, (byte) 0x3C,
+ (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE, (byte) 0x69, (byte) 0x3A,
+ (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5, (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75, (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93,
+ (byte) 0xC9, (byte) 0x69, (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x34
+ };
+
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected geographical scope decoded",
+ SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE, msg.getGeographicalScope());
+ }
+
+ @Test
+ public void testGetMessageBody7Bit() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x40, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91, (byte) 0xCB,
+ (byte) 0xE6,
+ (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07, (byte) 0x85, (byte) 0xD9,
+ (byte) 0x70,
+ (byte) 0x74, (byte) 0x58, (byte) 0x5C, (byte) 0xA6, (byte) 0x83, (byte) 0xDA,
+ (byte) 0xE5,
+ (byte) 0xF9, (byte) 0x3C, (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE,
+ (byte) 0x69,
+ (byte) 0x3A, (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5,
+ (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75,
+ (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93, (byte) 0xC9,
+ (byte) 0x69,
+ (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBody7BitUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x32, (byte) 0xC0, (byte) 0x00, (byte) 0x40,
+
+ (byte) 0x01,
+
+ (byte) 0x41, (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91,
+ (byte) 0xCB, (byte) 0xE6, (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07,
+ (byte) 0x85, (byte) 0xD9, (byte) 0x70, (byte) 0x74, (byte) 0x58, (byte) 0x5C,
+ (byte) 0xA6, (byte) 0x83, (byte) 0xDA, (byte) 0xE5, (byte) 0xF9, (byte) 0x3C,
+ (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE, (byte) 0x69, (byte) 0x3A,
+ (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5, (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75, (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93,
+ (byte) 0xC9, (byte) 0x69, (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x34
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBody7BitMultipageUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xC0, (byte) 0x00, (byte) 0x40,
+
+ (byte) 0x02,
+
+ (byte) 0xC6, (byte) 0xB4, (byte) 0x7C, (byte) 0x4E, (byte) 0x07, (byte) 0xC1,
+ (byte) 0xC3, (byte) 0xE7, (byte) 0xF2, (byte) 0xAA, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34,
+ (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x0A,
+
+ (byte) 0xD3, (byte) 0xF2, (byte) 0xF8, (byte) 0xED, (byte) 0x26, (byte) 0x83,
+ (byte) 0xE0, (byte) 0xE1, (byte) 0x73, (byte) 0xB9, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34,
+ (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x0A
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected multipage 7-bit string decoded",
+ "First page+Second page",
+ msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBody7BitFull() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x40, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91, (byte) 0xCB,
+ (byte) 0xE6,
+ (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07, (byte) 0x85, (byte) 0xD9,
+ (byte) 0x70,
+ (byte) 0x74, (byte) 0x58, (byte) 0x5C, (byte) 0xA6, (byte) 0x83, (byte) 0xDA,
+ (byte) 0xE5,
+ (byte) 0xF9, (byte) 0x3C, (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xC4,
+ (byte) 0xE5,
+ (byte) 0xB4, (byte) 0xFB, (byte) 0x0C, (byte) 0x2A, (byte) 0xE3, (byte) 0xC3,
+ (byte) 0x63,
+ (byte) 0x3A, (byte) 0x3B, (byte) 0x0F, (byte) 0xCA, (byte) 0xCD, (byte) 0x40,
+ (byte) 0x63,
+ (byte) 0x74, (byte) 0x58, (byte) 0x1E, (byte) 0x1E, (byte) 0xD3, (byte) 0xCB,
+ (byte) 0xF2,
+ (byte) 0x39, (byte) 0x88, (byte) 0xFD, (byte) 0x76, (byte) 0x9F, (byte) 0x59,
+ (byte) 0xA0,
+ (byte) 0x76, (byte) 0x39, (byte) 0xEC, (byte) 0x4E, (byte) 0xBB, (byte) 0xCF,
+ (byte) 0x20,
+ (byte) 0x3A, (byte) 0xBA, (byte) 0x2C, (byte) 0x2F, (byte) 0x83, (byte) 0xD2,
+ (byte) 0x73,
+ (byte) 0x90, (byte) 0xFB, (byte) 0x0D, (byte) 0x82, (byte) 0x87, (byte) 0xC9,
+ (byte) 0xE4,
+ (byte) 0xB4, (byte) 0xFB, (byte) 0x1C, (byte) 0x02
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals(
+ "Unexpected 7-bit string decoded",
+ "A GSM default alphabet message being exactly 93 characters long, "
+ + "meaning there is no padding!",
+ msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBody7BitFullUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x32, (byte) 0xC0, (byte) 0x00, (byte) 0x40,
+
+ (byte) 0x01,
+
+ (byte) 0x41, (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91,
+ (byte) 0xCB, (byte) 0xE6, (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07,
+ (byte) 0x85, (byte) 0xD9, (byte) 0x70, (byte) 0x74, (byte) 0x58, (byte) 0x5C,
+ (byte) 0xA6, (byte) 0x83, (byte) 0xDA, (byte) 0xE5, (byte) 0xF9, (byte) 0x3C,
+ (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xC4, (byte) 0xE5, (byte) 0xB4,
+ (byte) 0xFB, (byte) 0x0C, (byte) 0x2A, (byte) 0xE3, (byte) 0xC3, (byte) 0x63,
+ (byte) 0x3A, (byte) 0x3B, (byte) 0x0F, (byte) 0xCA, (byte) 0xCD, (byte) 0x40,
+ (byte) 0x63, (byte) 0x74, (byte) 0x58, (byte) 0x1E, (byte) 0x1E, (byte) 0xD3,
+ (byte) 0xCB, (byte) 0xF2, (byte) 0x39, (byte) 0x88, (byte) 0xFD, (byte) 0x76,
+ (byte) 0x9F, (byte) 0x59, (byte) 0xA0, (byte) 0x76, (byte) 0x39, (byte) 0xEC,
+ (byte) 0x4E, (byte) 0xBB, (byte) 0xCF, (byte) 0x20, (byte) 0x3A, (byte) 0xBA,
+ (byte) 0x2C, (byte) 0x2F, (byte) 0x83, (byte) 0xD2, (byte) 0x73, (byte) 0x90,
+ (byte) 0xFB, (byte) 0x0D, (byte) 0x82, (byte) 0x87, (byte) 0xC9, (byte) 0xE4,
+ (byte) 0xB4, (byte) 0xFB, (byte) 0x1C, (byte) 0x02,
+
+ (byte) 0x52
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals(
+ "Unexpected 7-bit string decoded",
+ "A GSM default alphabet message being exactly 93 characters long, "
+ + "meaning there is no padding!",
+ msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBody7BitWithLanguage() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x04, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91, (byte) 0xCB,
+ (byte) 0xE6,
+ (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07, (byte) 0x85, (byte) 0xD9,
+ (byte) 0x70,
+ (byte) 0x74, (byte) 0x58, (byte) 0x5C, (byte) 0xA6, (byte) 0x83, (byte) 0xDA,
+ (byte) 0xE5,
+ (byte) 0xF9, (byte) 0x3C, (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE,
+ (byte) 0x69,
+ (byte) 0x3A, (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5,
+ (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75,
+ (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93, (byte) 0xC9,
+ (byte) 0x69,
+ (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "es", msg.getLanguageCode());
+ }
+
+ @Test
+ public void testGetMessageBody7BitWithLanguageInBody() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x10, (byte) 0x11,
+ (byte) 0x73,
+ (byte) 0x7B, (byte) 0x23, (byte) 0x08, (byte) 0x3A, (byte) 0x4E, (byte) 0x9B,
+ (byte) 0x20,
+ (byte) 0x72, (byte) 0xD9, (byte) 0x1C, (byte) 0xAE, (byte) 0xB3, (byte) 0xE9,
+ (byte) 0xA0,
+ (byte) 0x30, (byte) 0x1B, (byte) 0x8E, (byte) 0x0E, (byte) 0x8B, (byte) 0xCB,
+ (byte) 0x74,
+ (byte) 0x50, (byte) 0xBB, (byte) 0x3C, (byte) 0x9F, (byte) 0x87, (byte) 0xCF,
+ (byte) 0x65,
+ (byte) 0xD0, (byte) 0x3D, (byte) 0x4D, (byte) 0x47, (byte) 0x83, (byte) 0xC6,
+ (byte) 0x61,
+ (byte) 0xB9, (byte) 0x3C, (byte) 0x1D, (byte) 0x3E, (byte) 0x97, (byte) 0x41,
+ (byte) 0xF2,
+ (byte) 0x32, (byte) 0xBD, (byte) 0x2E, (byte) 0x77, (byte) 0x83, (byte) 0xE0,
+ (byte) 0x61,
+ (byte) 0x32, (byte) 0x39, (byte) 0xED, (byte) 0x3E, (byte) 0x37, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+ }
+
+ @Test
+ public void testGetMessageBody7BitWithLanguageInBodyUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x32, (byte) 0xC0, (byte) 0x00, (byte) 0x10,
+
+ (byte) 0x01,
+
+ (byte) 0x73, (byte) 0x7B, (byte) 0x23, (byte) 0x08, (byte) 0x3A, (byte) 0x4E,
+ (byte) 0x9B, (byte) 0x20, (byte) 0x72, (byte) 0xD9, (byte) 0x1C, (byte) 0xAE,
+ (byte) 0xB3, (byte) 0xE9, (byte) 0xA0, (byte) 0x30, (byte) 0x1B, (byte) 0x8E,
+ (byte) 0x0E, (byte) 0x8B, (byte) 0xCB, (byte) 0x74, (byte) 0x50, (byte) 0xBB,
+ (byte) 0x3C, (byte) 0x9F, (byte) 0x87, (byte) 0xCF, (byte) 0x65, (byte) 0xD0,
+ (byte) 0x3D, (byte) 0x4D, (byte) 0x47, (byte) 0x83, (byte) 0xC6, (byte) 0x61,
+ (byte) 0xB9, (byte) 0x3C, (byte) 0x1D, (byte) 0x3E, (byte) 0x97, (byte) 0x41,
+ (byte) 0xF2, (byte) 0x32, (byte) 0xBD, (byte) 0x2E, (byte) 0x77, (byte) 0x83,
+ (byte) 0xE0, (byte) 0x61, (byte) 0x32, (byte) 0x39, (byte) 0xED, (byte) 0x3E,
+ (byte) 0x37, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x37
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+ }
+
+ @Test
+ public void testGetMessageBody8Bit() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x44, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
+ (byte) 0x41,
+ (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("8-bit message body should be empty", "", msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBodyUcs2() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x48, (byte) 0x11,
+ (byte) 0x00,
+ (byte) 0x41, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x55, (byte) 0x00,
+ (byte) 0x43,
+ (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x20,
+ (byte) 0x00,
+ (byte) 0x6D, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x73, (byte) 0x00,
+ (byte) 0x73,
+ (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x67, (byte) 0x00, (byte) 0x65,
+ (byte) 0x00,
+ (byte) 0x20, (byte) 0x00, (byte) 0x63, (byte) 0x00, (byte) 0x6F, (byte) 0x00,
+ (byte) 0x6E,
+ (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x69,
+ (byte) 0x00,
+ (byte) 0x6E, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x6E, (byte) 0x00,
+ (byte) 0x67,
+ (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x20,
+ (byte) 0x04,
+ (byte) 0x34, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x63, (byte) 0x00,
+ (byte) 0x68,
+ (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00,
+ (byte) 0x63, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x65, (byte) 0x00,
+ (byte) 0x72,
+ (byte) 0x00, (byte) 0x0D, (byte) 0x00, (byte) 0x0D
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBodyUcs2Umts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x32, (byte) 0xC0, (byte) 0x00, (byte) 0x48,
+
+ (byte) 0x01,
+
+ (byte) 0x00, (byte) 0x41, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x55,
+ (byte) 0x00, (byte) 0x43, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x32,
+ (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x6D, (byte) 0x00, (byte) 0x65,
+ (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00, (byte) 0x67, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x20,
+ (byte) 0x00, (byte) 0x63, (byte) 0x00, (byte) 0x6F, (byte) 0x00, (byte) 0x6E,
+ (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x69,
+ (byte) 0x00, (byte) 0x6E, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x6E,
+ (byte) 0x00, (byte) 0x67, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00, (byte) 0x20, (byte) 0x04, (byte) 0x34, (byte) 0x00, (byte) 0x20,
+ (byte) 0x00, (byte) 0x63, (byte) 0x00, (byte) 0x68, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x63,
+ (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x72,
+ (byte) 0x00, (byte) 0x0D, (byte) 0x00, (byte) 0x0D,
+
+ (byte) 0x4E
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBodyUcs2MultipageUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x32, (byte) 0xC0, (byte) 0x00, (byte) 0x48,
+
+ (byte) 0x02,
+
+ (byte) 0x00, (byte) 0x41, (byte) 0x00, (byte) 0x41, (byte) 0x00, (byte) 0x41,
+ (byte) 0x00, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+
+ (byte) 0x06,
+
+ (byte) 0x00, (byte) 0x42, (byte) 0x00, (byte) 0x42, (byte) 0x00, (byte) 0x42,
+ (byte) 0x00, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+ (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+
+ (byte) 0x06
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected multipage UCS2 string decoded",
+ "AAABBB", msg.getMessageBody());
+ }
+
+ @Test
+ public void testGetMessageBodyUcs2WithLanguageInBody() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x11, (byte) 0x11,
+ (byte) 0x78,
+ (byte) 0x3C, (byte) 0x00, (byte) 0x41, (byte) 0x00, (byte) 0x20, (byte) 0x00,
+ (byte) 0x55,
+ (byte) 0x00, (byte) 0x43, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x32,
+ (byte) 0x00,
+ (byte) 0x20, (byte) 0x00, (byte) 0x6D, (byte) 0x00, (byte) 0x65, (byte) 0x00,
+ (byte) 0x73,
+ (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x67,
+ (byte) 0x00,
+ (byte) 0x65, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x63, (byte) 0x00,
+ (byte) 0x6F,
+ (byte) 0x00, (byte) 0x6E, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00,
+ (byte) 0x69, (byte) 0x00, (byte) 0x6E, (byte) 0x00, (byte) 0x69, (byte) 0x00,
+ (byte) 0x6E,
+ (byte) 0x00, (byte) 0x67, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00,
+ (byte) 0x20, (byte) 0x04, (byte) 0x34, (byte) 0x00, (byte) 0x20, (byte) 0x00,
+ (byte) 0x63,
+ (byte) 0x00, (byte) 0x68, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x72,
+ (byte) 0x00,
+ (byte) 0x61, (byte) 0x00, (byte) 0x63, (byte) 0x00, (byte) 0x74, (byte) 0x00,
+ (byte) 0x65,
+ (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x0D
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+ }
+
+ @Test
+ public void testGetMessageBodyUcs2WithLanguageInBodyUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x00, (byte) 0x32, (byte) 0xC0, (byte) 0x00, (byte) 0x11,
+
+ (byte) 0x01,
+
+ (byte) 0x78, (byte) 0x3C, (byte) 0x00, (byte) 0x41, (byte) 0x00, (byte) 0x20,
+ (byte) 0x00, (byte) 0x55, (byte) 0x00, (byte) 0x43, (byte) 0x00, (byte) 0x53,
+ (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x6D,
+ (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x73,
+ (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x67, (byte) 0x00, (byte) 0x65,
+ (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x63, (byte) 0x00, (byte) 0x6F,
+ (byte) 0x00, (byte) 0x6E, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x6E, (byte) 0x00, (byte) 0x69,
+ (byte) 0x00, (byte) 0x6E, (byte) 0x00, (byte) 0x67, (byte) 0x00, (byte) 0x20,
+ (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x20, (byte) 0x04, (byte) 0x34,
+ (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x63, (byte) 0x00, (byte) 0x68,
+ (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x61,
+ (byte) 0x00, (byte) 0x63, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x65,
+ (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x0D,
+
+ (byte) 0x50
+ };
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+ }
+
+ @Test
+ public void testGetMessageIdentifier() {
+ byte[] pdu = {
+ (byte) 0xC0, (byte) 0x00, (byte) 0x30, (byte) 0x39, (byte) 0x40, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91, (byte) 0xCB,
+ (byte) 0xE6,
+ (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07, (byte) 0x85, (byte) 0xD9,
+ (byte) 0x70,
+ (byte) 0x74, (byte) 0x58, (byte) 0x5C, (byte) 0xA6, (byte) 0x83, (byte) 0xDA,
+ (byte) 0xE5,
+ (byte) 0xF9, (byte) 0x3C, (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE,
+ (byte) 0x69,
+ (byte) 0x3A, (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5,
+ (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75,
+ (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93, (byte) 0xC9,
+ (byte) 0x69,
+ (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00
+ };
+
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected message identifier decoded", 12345, msg.getServiceCategory());
+ }
+
+ @Test
+ public void testGetMessageIdentifierUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x30, (byte) 0x39, (byte) 0x2A, (byte) 0xA5, (byte) 0x40,
+
+ (byte) 0x01,
+
+ (byte) 0x41, (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91,
+ (byte) 0xCB, (byte) 0xE6, (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07,
+ (byte) 0x85, (byte) 0xD9, (byte) 0x70, (byte) 0x74, (byte) 0x58, (byte) 0x5C,
+ (byte) 0xA6, (byte) 0x83, (byte) 0xDA, (byte) 0xE5, (byte) 0xF9, (byte) 0x3C,
+ (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE, (byte) 0x69, (byte) 0x3A,
+ (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5, (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75, (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93,
+ (byte) 0xC9, (byte) 0x69, (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x34
+ };
+
+ SmsCbMessage msg = createFromPdu(pdu);
+
+ assertEquals("Unexpected message identifier decoded", 12345, msg.getServiceCategory());
+ }
+
+ @Test
+ public void testGetMessageCode() {
+ byte[] pdu = {
+ (byte) 0x2A, (byte) 0xA5, (byte) 0x30, (byte) 0x39, (byte) 0x40, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91, (byte) 0xCB,
+ (byte) 0xE6,
+ (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07, (byte) 0x85, (byte) 0xD9,
+ (byte) 0x70,
+ (byte) 0x74, (byte) 0x58, (byte) 0x5C, (byte) 0xA6, (byte) 0x83, (byte) 0xDA,
+ (byte) 0xE5,
+ (byte) 0xF9, (byte) 0x3C, (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE,
+ (byte) 0x69,
+ (byte) 0x3A, (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5,
+ (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75,
+ (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93, (byte) 0xC9,
+ (byte) 0x69,
+ (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00
+ };
+
+ SmsCbMessage msg = createFromPdu(pdu);
+ int messageCode = (msg.getSerialNumber() & 0x3ff0) >> 4;
+
+ assertEquals("Unexpected message code decoded", 682, messageCode);
+ }
+
+ @Test
+ public void testGetMessageCodeUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x30, (byte) 0x39, (byte) 0x2A, (byte) 0xA5, (byte) 0x40,
+
+ (byte) 0x01,
+
+ (byte) 0x41, (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91,
+ (byte) 0xCB, (byte) 0xE6, (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07,
+ (byte) 0x85, (byte) 0xD9, (byte) 0x70, (byte) 0x74, (byte) 0x58, (byte) 0x5C,
+ (byte) 0xA6, (byte) 0x83, (byte) 0xDA, (byte) 0xE5, (byte) 0xF9, (byte) 0x3C,
+ (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE, (byte) 0x69, (byte) 0x3A,
+ (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5, (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75, (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93,
+ (byte) 0xC9, (byte) 0x69, (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x34
+ };
+
+ SmsCbMessage msg = createFromPdu(pdu);
+ int messageCode = (msg.getSerialNumber() & 0x3ff0) >> 4;
+
+ assertEquals("Unexpected message code decoded", 682, messageCode);
+ }
+
+ @Test
+ public void testGetUpdateNumber() {
+ byte[] pdu = {
+ (byte) 0x2A, (byte) 0xA5, (byte) 0x30, (byte) 0x39, (byte) 0x40, (byte) 0x11,
+ (byte) 0x41,
+ (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91, (byte) 0xCB,
+ (byte) 0xE6,
+ (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07, (byte) 0x85, (byte) 0xD9,
+ (byte) 0x70,
+ (byte) 0x74, (byte) 0x58, (byte) 0x5C, (byte) 0xA6, (byte) 0x83, (byte) 0xDA,
+ (byte) 0xE5,
+ (byte) 0xF9, (byte) 0x3C, (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE,
+ (byte) 0x69,
+ (byte) 0x3A, (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5,
+ (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75,
+ (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93, (byte) 0xC9,
+ (byte) 0x69,
+ (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A,
+ (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00
+ };
+
+ SmsCbMessage msg = createFromPdu(pdu);
+ int updateNumber = msg.getSerialNumber() & 0x000f;
+
+ assertEquals("Unexpected update number decoded", 5, updateNumber);
+ }
+
+ @Test
+ public void testGetUpdateNumberUmts() {
+ byte[] pdu = {
+ (byte) 0x01, (byte) 0x30, (byte) 0x39, (byte) 0x2A, (byte) 0xA5, (byte) 0x40,
+
+ (byte) 0x01,
+
+ (byte) 0x41, (byte) 0xD0, (byte) 0x71, (byte) 0xDA, (byte) 0x04, (byte) 0x91,
+ (byte) 0xCB, (byte) 0xE6, (byte) 0x70, (byte) 0x9D, (byte) 0x4D, (byte) 0x07,
+ (byte) 0x85, (byte) 0xD9, (byte) 0x70, (byte) 0x74, (byte) 0x58, (byte) 0x5C,
+ (byte) 0xA6, (byte) 0x83, (byte) 0xDA, (byte) 0xE5, (byte) 0xF9, (byte) 0x3C,
+ (byte) 0x7C, (byte) 0x2E, (byte) 0x83, (byte) 0xEE, (byte) 0x69, (byte) 0x3A,
+ (byte) 0x1A, (byte) 0x34, (byte) 0x0E, (byte) 0xCB, (byte) 0xE5, (byte) 0xE9,
+ (byte) 0xF0, (byte) 0xB9, (byte) 0x0C, (byte) 0x92, (byte) 0x97, (byte) 0xE9,
+ (byte) 0x75, (byte) 0xB9, (byte) 0x1B, (byte) 0x04, (byte) 0x0F, (byte) 0x93,
+ (byte) 0xC9, (byte) 0x69, (byte) 0xF7, (byte) 0xB9, (byte) 0xD1, (byte) 0x68,
+ (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3, (byte) 0xD1,
+ (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46, (byte) 0xA3,
+ (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D, (byte) 0x46,
+ (byte) 0xA3, (byte) 0xD1, (byte) 0x68, (byte) 0x34, (byte) 0x1A, (byte) 0x8D,
+ (byte) 0x46, (byte) 0xA3, (byte) 0xD1, (byte) 0x00,
+
+ (byte) 0x34
+ };
+
+ SmsCbMessage msg = createFromPdu(pdu);
+ int updateNumber = msg.getSerialNumber() & 0x000f;
+
+ assertEquals("Unexpected update number decoded", 5, updateNumber);
+ }
+
+ /* ETWS Test message including header */
+ private static final byte[] etwsMessageNormal = hexStringToBytes("000011001101"
+ + "0D0A5BAE57CE770C531790E85C716CBF3044573065B930675730"
+ + "9707767A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09"
+ + "0000000000000000000000000000");
+
+ private static final byte[] etwsMessageCancel = hexStringToBytes("000011001101"
+ + "0D0A5148307B3069002800310030003A0035"
+ + "00320029306E7DCA602557309707901F5831309253D66D883057307E3059FF086C178C615E81FF09"
+ + "00000000000000000000000000000000000000000000");
+
+ private static final byte[] etwsMessageTest = hexStringToBytes("000011031101"
+ + "0D0A5BAE57CE770C531790E85C716CBF3044"
+ + "573065B9306757309707300263FA308C306B5099304830664E0B30553044FF086C178C615E81FF09"
+ + "00000000000000000000000000000000000000000000");
+
+ // FIXME: add example of ETWS primary notification PDU
+
+ @Test
+ public void testEtwsMessageNormal() {
+ SmsCbMessage msg = createFromPdu(etwsMessageNormal);
+ Rlog.d(TAG, msg.toString());
+ assertEquals("GS mismatch", 0, msg.getGeographicalScope());
+ assertEquals("serial number mismatch", 0, msg.getSerialNumber());
+ assertEquals("message ID mismatch", 0x1100, msg.getServiceCategory());
+ assertEquals("warning type mismatch", SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE,
+ msg.getEtwsWarningInfo().getWarningType());
+ }
+
+ @Test
+ public void testEtwsMessageCancel() {
+ SmsCbMessage msg = createFromPdu(etwsMessageCancel);
+ Rlog.d(TAG, msg.toString());
+ assertEquals("GS mismatch", 0, msg.getGeographicalScope());
+ assertEquals("serial number mismatch", 0, msg.getSerialNumber());
+ assertEquals("message ID mismatch", 0x1100, msg.getServiceCategory());
+ assertEquals("warning type mismatch", SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE,
+ msg.getEtwsWarningInfo().getWarningType());
+ }
+
+ @Test
+ public void testEtwsMessageTest() {
+ SmsCbMessage msg = createFromPdu(etwsMessageTest);
+ Rlog.d(TAG, msg.toString());
+ assertEquals("GS mismatch", 0, msg.getGeographicalScope());
+ assertEquals("serial number mismatch", 0, msg.getSerialNumber());
+ assertEquals("message ID mismatch", 0x1103, msg.getServiceCategory());
+ assertEquals("warning type mismatch", SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE,
+ msg.getEtwsWarningInfo().getWarningType());
+ }
+
+ // Make sure we don't throw an exception if we feed random data to the PDU parser.
+ @Test
+ public void testRandomPdus() {
+ Random r = new Random(94040);
+ for (int run = 0; run < 10000; run++) {
+ int len = r.nextInt(140);
+ byte[] data = new byte[len];
+ for (int i = 0; i < len; i++) {
+ data[i] = (byte) r.nextInt(256);
+ }
+ try {
+ // this should return a SmsCbMessage object or null for invalid data
+ SmsCbMessage msg = createFromPdu(data);
+ } catch (Exception e) {
+ Rlog.d(TAG, "exception thrown", e);
+ fail("Exception in decoder at run " + run + " length " + len + ": " + e);
+ }
+ }
+ }
+}