Merge "Do not use sendOrderedBroadcastAsUser"
diff --git a/Android.bp b/Android.bp
index 49e0d97..6253385 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,9 +33,19 @@
      manifest: "AndroidManifest_Platform.xml",
 }
 
+// used to share common constants between cellbroadcastservice and cellbroadcastreceier
 filegroup {
     name: "cellbroadcast-constants-shared-srcs",
     srcs: [
         "src/com/android/cellbroadcastservice/SmsCbConstants.java",
     ],
+}
+
+// used to share src with unit test app
+filegroup {
+    name: "cellbroadcast-shared-srcs",
+    srcs: [
+        "src/**/*.java",
+        ":framework-cellbroadcast-shared-srcs",
+    ],
 }
\ No newline at end of file
diff --git a/src/com/android/cellbroadcastservice/CellBroadcastHandler.java b/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
index 2efbe47..466ae94 100644
--- a/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
+++ b/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
@@ -423,6 +423,8 @@
             logd("Device location is outside the broadcast area "
                     + CbGeoUtils.encodeGeometriesToString(broadcastArea));
         }
+
+        sendMessage(EVENT_BROADCAST_NOT_REQUIRED);
     }
 
     /**
diff --git a/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java b/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
index 3361c16..b8b6f5a 100644
--- a/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
+++ b/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
@@ -212,7 +212,7 @@
     private static Pair<Integer, List<Geometry>> parseWarningAreaCoordinates(
             byte[] pdu, int wacOffset) {
         // little-endian
-        int wacDataLength = (pdu[wacOffset + 1] << 8) | pdu[wacOffset];
+        int wacDataLength = ((pdu[wacOffset + 1] & 0xff) << 8) | (pdu[wacOffset] & 0xff);
         int offset = wacOffset + 2;
 
         if (offset + wacDataLength > pdu.length) {
diff --git a/src/com/android/cellbroadcastservice/WakeLockStateMachine.java b/src/com/android/cellbroadcastservice/WakeLockStateMachine.java
index edb392e..789534d 100644
--- a/src/com/android/cellbroadcastservice/WakeLockStateMachine.java
+++ b/src/com/android/cellbroadcastservice/WakeLockStateMachine.java
@@ -51,6 +51,9 @@
     /** Release wakelock after a short timeout when returning to idle state. */
     static final int EVENT_RELEASE_WAKE_LOCK = 3;
 
+    /** Broadcast not required due to geo-fencing check */
+    static final int EVENT_BROADCAST_NOT_REQUIRED = 4;
+
     @UnsupportedAppUsage
     protected Context mContext;
 
@@ -148,13 +151,14 @@
         @Override
         public void exit() {
             mWakeLock.acquire();
-            if (DBG) log("acquired wakelock, leaving Idle state");
+            if (DBG) log("Idle: acquired wakelock, leaving Idle state");
         }
 
         @Override
         public boolean processMessage(Message msg) {
             switch (msg.what) {
                 case EVENT_NEW_SMS_MESSAGE:
+                    log("Idle: new cell broadcast message");
                     // transition to waiting state if we sent a broadcast
                     if (handleSmsMessage(msg)) {
                         transitionTo(mWaitingState);
@@ -162,9 +166,12 @@
                     return HANDLED;
 
                 case EVENT_RELEASE_WAKE_LOCK:
+                    log("Idle: release wakelock");
                     releaseWakeLock();
                     return HANDLED;
-
+                case EVENT_BROADCAST_NOT_REQUIRED:
+                    log("Idle: broadcast not required");
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
@@ -180,19 +187,25 @@
         public boolean processMessage(Message msg) {
             switch (msg.what) {
                 case EVENT_NEW_SMS_MESSAGE:
-                    log("deferring message until return to idle");
+                    log("Waiting: deferring message until return to idle");
                     deferMessage(msg);
                     return HANDLED;
 
                 case EVENT_BROADCAST_COMPLETE:
-                    log("broadcast complete, returning to idle");
+                    log("Waiting: broadcast complete, returning to idle");
                     transitionTo(mIdleState);
                     return HANDLED;
 
                 case EVENT_RELEASE_WAKE_LOCK:
+                    log("Waiting: release wakelock");
                     releaseWakeLock();
                     return HANDLED;
-
+                case EVENT_BROADCAST_NOT_REQUIRED:
+                    log("Waiting: broadcast not required");
+                    if (mReceiverCount.get() == 0) {
+                        transitionTo(mIdleState);
+                    }
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
diff --git a/tests/Android.bp b/tests/Android.bp
index 1097217..f59dde6 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -13,7 +13,7 @@
         "android.test.base",
         "android.test.mock",
     ],
-    srcs: ["src/**/*.java"],
+    srcs: ["src/**/*.java", ":cellbroadcast-shared-srcs"],
     platform_apis: true,
     test_suites: ["device-tests"],
     certificate: "platform",
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 445a960..7403d26 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -22,7 +22,7 @@
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.cellbroadcastservice"
+        android:targetPackage="com.android.cellbroadcastservice.tests"
         android:label="Tests for CellBroadcastService">
     </instrumentation>
 </manifest>
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 8b61a43..022ff36 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -20,7 +20,7 @@
 
     <option name="test-suite-tag" value="apct" />
     <option name="test-tag" value="CellBroadcastServiceTests" />
-    <test class="com.android.tradefed.testtype.InstrumentationTest" >
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.cellbroadcastservice.tests" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
diff --git a/tests/src/com/android/cellbroadcastservice/CellBroadcastServiceTestBase.java b/tests/src/com/android/cellbroadcastservice/CellBroadcastServiceTestBase.java
index 9246f35..04eda63 100644
--- a/tests/src/com/android/cellbroadcastservice/CellBroadcastServiceTestBase.java
+++ b/tests/src/com/android/cellbroadcastservice/CellBroadcastServiceTestBase.java
@@ -147,4 +147,50 @@
         mInstanceKeys.clear();
         mOldInstances.clear();
     }
+
+    /**
+     * Converts a hex String to a byte array.
+     *
+     * @param s A string of hexadecimal characters, must be an even number of
+     *          chars long
+     *
+     * @return byte array representation
+     *
+     * @throws RuntimeException on invalid format
+     */
+    public static byte[] hexStringToBytes(String s) {
+        byte[] ret;
+
+        if (s == null) return null;
+
+        int sz = s.length();
+
+        ret = new byte[sz / 2];
+
+        for (int i = 0; i < sz; i += 2) {
+            ret[i / 2] = (byte) ((hexCharToInt(s.charAt(i)) << 4) | hexCharToInt(s.charAt(i + 1)));
+        }
+
+        return ret;
+    }
+
+    /**
+     * Converts a hex char to its integer value
+     *
+     * @param c A single hexadecimal character. Must be in one of these ranges:
+     *          - '0' to '9', or
+     *          - 'a' to 'f', or
+     *          - 'A' to 'F'
+     *
+     * @return the integer representation of {@code c}
+     *
+     * @throws RuntimeException on invalid character
+     */
+    private static int hexCharToInt(char c) {
+        if (c >= '0' && c <= '9') return (c - '0');
+        if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
+        if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
+
+        throw new RuntimeException("invalid hex char '" + c + "'");
+    }
 }
diff --git a/tests/src/com/android/cellbroadcastservice/GsmSmsCbMessageTest.java b/tests/src/com/android/cellbroadcastservice/GsmSmsCbMessageTest.java
new file mode 100644
index 0000000..1c9f4e2
--- /dev/null
+++ b/tests/src/com/android/cellbroadcastservice/GsmSmsCbMessageTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 {
+
+    @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("GsmSmsCbMessageTest", "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);
+        }
+    }
+}