Add Telephony satellite on-device access control module and tools

Bug: 313773568
Test: atest SatelliteToolsTests TeleServiceTests

Change-Id: I096310b71ec92beffed42321ed4205ac085dcf8c
diff --git a/tests/Android.bp b/tests/Android.bp
index d2288d0..3015f76 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -50,6 +50,10 @@
         "testables",
         "platform-compat-test-rules",
         "flag-junit",
+        "satellite-s2storage-rw",
+        "satellite-s2storage-testutils",
+        "s2-geometry-library-java",
+        "telephony-satellite",
     ],
 
     test_suites: [
diff --git a/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
new file mode 100644
index 0000000..84c233a
--- /dev/null
+++ b/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2023 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.phone.satellite.accesscontrol;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.storage.s2.S2LevelRange;
+import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
+import com.android.telephony.sats2range.utils.TestUtils;
+import com.android.telephony.sats2range.write.SatS2RangeFileWriter;
+
+import com.google.common.geometry.S2CellId;
+import com.google.common.geometry.S2LatLng;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class S2RangeSatelliteOnDeviceAccessControllerTest {
+    private File mFile;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mFile = File.createTempFile("test", ".data");
+        assertTrue(mFile.exists());
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        if (mFile != null && mFile.exists()) {
+            assertTrue(mFile.delete());
+        }
+    }
+
+    @Test
+    public void testSatelliteAccessControl_AllowedList() throws Exception {
+        testSatelliteAccessControl(true);
+    }
+
+    @Test
+    public void testSatelliteAccessControl_DisallowedList() throws Exception {
+        testSatelliteAccessControl(false);
+    }
+
+    private void testSatelliteAccessControl(boolean isAllowedList) throws Exception {
+        SatS2RangeFileFormat fileFormat = null;
+        try {
+            fileFormat = createSatS2File(mFile, isAllowedList);
+        } catch (Exception ex) {
+            fail("Got unexpected exception in createSatS2File, ex=" + ex);
+        }
+
+        // Validate the output block file
+        SatelliteOnDeviceAccessController accessController = null;
+        try {
+            accessController = SatelliteOnDeviceAccessController.create(mFile);
+
+            // Verify an edge cell of range 1 not in the output file
+            S2CellId s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, 999));
+            S2LatLng s2LatLng = s2CellId.toLatLng();
+            SatelliteOnDeviceAccessController.LocationToken locationToken =
+                    accessController.createLocationTokenForLatLng(
+                            s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            boolean isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+            assertTrue(isAllowed != isAllowedList);
+
+            // Verify cells in range1 present in the output file
+            for (int suffix = 1000; suffix < 2000; suffix++) {
+                s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, suffix));
+                s2LatLng = s2CellId.toLatLng();
+
+                // Lookup using location token
+                locationToken = accessController.createLocationTokenForLatLng(
+                                s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+                isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+                assertTrue(isAllowed == isAllowedList);
+            }
+
+            // Verify the middle cell not in the output file
+            s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, 2000));
+            s2LatLng = s2CellId.toLatLng();
+            locationToken = accessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+            assertTrue(isAllowed != isAllowedList);
+
+            // Verify cells in range2 present in the output file
+            for (int suffix = 2001; suffix < 3000; suffix++) {
+                s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, suffix));
+                s2LatLng = s2CellId.toLatLng();
+                locationToken = accessController.createLocationTokenForLatLng(
+                        s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+                isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+                assertTrue(isAllowed == isAllowedList);
+            }
+
+            // Verify an edge cell of range 2 not in the output file
+            s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, 3000));
+            s2LatLng = s2CellId.toLatLng();
+            locationToken = accessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+            assertTrue(isAllowed != isAllowedList);
+
+            // Verify an edge cell of range 3 not in the output file
+            s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, 999));
+            s2LatLng = s2CellId.toLatLng();
+            locationToken = accessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+            assertTrue(isAllowed != isAllowedList);
+
+            // Verify cells in range1 present in the output file
+            for (int suffix = 1000; suffix < 2000; suffix++) {
+                s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, suffix));
+                s2LatLng = s2CellId.toLatLng();
+                locationToken = accessController.createLocationTokenForLatLng(
+                        s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+                isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+                assertTrue(isAllowed == isAllowedList);
+            }
+
+            // Verify an edge cell of range 3 not in the output file
+            s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, 2000));
+            s2LatLng = s2CellId.toLatLng();
+            locationToken = accessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
+            assertTrue(isAllowed != isAllowedList);
+        } catch (Exception ex) {
+            fail("Unexpected exception when validating the output ex=" + ex);
+        } finally {
+            if (accessController != null) {
+                accessController.close();
+            }
+        }
+    }
+
+    private SatS2RangeFileFormat createSatS2File(
+            File file, boolean isAllowedList) throws Exception {
+        SatS2RangeFileFormat fileFormat;
+        S2LevelRange range1, range2, range3;
+        try (SatS2RangeFileWriter satS2RangeFileWriter = SatS2RangeFileWriter.open(
+                file, TestUtils.createS2RangeFileFormat(isAllowedList))) {
+            fileFormat = satS2RangeFileWriter.getFileFormat();
+
+            // Two ranges that share a prefix.
+            range1 = new S2LevelRange(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 1000),
+                    TestUtils.createCellId(fileFormat, 1, 1000, 2000));
+            range2 = new S2LevelRange(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 2001),
+                    TestUtils.createCellId(fileFormat, 1, 1000, 3000));
+            // This range has a different prefix, so will be in a different suffix table.
+            range3 = new S2LevelRange(
+                    TestUtils.createCellId(fileFormat, 1, 1001, 1000),
+                    TestUtils.createCellId(fileFormat, 1, 1001, 2000));
+
+            List<S2LevelRange> ranges = new ArrayList<>();
+            ranges.add(range1);
+            ranges.add(range2);
+            ranges.add(range3);
+            satS2RangeFileWriter.createSortedSuffixBlocks(ranges.iterator());
+        }
+        assertTrue(file.length() > 0);
+        return fileFormat;
+    }
+}