apkdmverity: enable AndroidTest

The Android test for apkdmverity is now enabled and the binary is
slightly modified to use Android-specific paths (e.g. /dev/block/loopX
instead of /dev/loopX).

Bug: 189785765
Test: cargo test
Test: adb root; adb shell setenforce 0; atest apkdmverity.test
Change-Id: Iaeebbefd961db289af3d2ba7492c1c3110f72c0c
diff --git a/apkverity/AndroidTest.xml b/apkverity/AndroidTest.xml
new file mode 100644
index 0000000..7eb77a4
--- /dev/null
+++ b/apkverity/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<configuration description="Config for apkdmverity tests">
+  <!--
+    Creating and configuring the loop devices and the device-mapper devices require root privilege.
+  -->
+  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+  <!--
+    We need to disable selinux because kernel (which is implementing the loop device) doesn't have
+    the privilege to read files on /data. Otherwise, we hit the following errors:
+
+    avc: denied { read } for comm="loop32"
+    path="/data/local/tmp/.tmp.ptPChH/test.apk.idsig" dev="dm-8" ino=2939
+    scontext=u:r:kernel:s0 tcontext=u:object_r:shell_data_file:s0
+    tclass=file
+  -->
+  <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer"/>
+
+  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+    <option name="push-file" key="apkdmverity.test" value="/data/local/tmp/apkdmverity.test" />
+  </target_preparer>
+
+  <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+    <option name="test-device-path" value="/data/local/tmp" />
+    <option name="module-name" value="apkdmverity.test" />
+  </test>
+</configuration>
diff --git a/apkverity/src/apksigv4.rs b/apkverity/src/apksigv4.rs
index 7d8f318..fef21a5 100644
--- a/apkverity/src/apksigv4.rs
+++ b/apkverity/src/apksigv4.rs
@@ -153,13 +153,10 @@
 
 #[cfg(test)]
 mod tests {
+    use crate::util::hexstring_from;
     use crate::*;
     use std::io::Cursor;
 
-    fn hexstring_from(s: &[u8]) -> String {
-        s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
-    }
-
     #[test]
     fn parse_idsig_file() {
         let idsig = Cursor::new(include_bytes!("../testdata/test.apk.idsig"));
diff --git a/apkverity/src/dm.rs b/apkverity/src/dm.rs
index 8828b0a..7ac72c8 100644
--- a/apkverity/src/dm.rs
+++ b/apkverity/src/dm.rs
@@ -126,11 +126,21 @@
 /// handle to "/dev/mapper/control".
 pub struct DeviceMapper(File);
 
+#[cfg(not(target_os = "android"))]
+const MAPPER_CONTROL: &str = "/dev/mapper/control";
+#[cfg(not(target_os = "android"))]
+const MAPPER_DEV_ROOT: &str = "/dev/mapper";
+
+#[cfg(target_os = "android")]
+const MAPPER_CONTROL: &str = "/dev/device-mapper";
+#[cfg(target_os = "android")]
+const MAPPER_DEV_ROOT: &str = "/dev/block/mapper";
+
 impl DeviceMapper {
     /// Constructs a new `DeviceMapper` entrypoint. This is essentially the same as opening
     /// "/dev/mapper/control".
     pub fn new() -> Result<DeviceMapper> {
-        let f = OpenOptions::new().read(true).write(true).open("/dev/mapper/control")?;
+        let f = OpenOptions::new().read(true).write(true).open(MAPPER_CONTROL)?;
         Ok(DeviceMapper(f))
     }
 
@@ -162,7 +172,7 @@
         dm_dev_suspend(&self, &mut data)?;
 
         // Step 4: wait unti the device is created and return the device path
-        let path = Path::new("/dev/mapper").join(&name);
+        let path = Path::new(MAPPER_DEV_ROOT).join(&name);
         wait_for_path(&path)?;
         Ok(path)
     }
diff --git a/apkverity/src/loopdevice.rs b/apkverity/src/loopdevice.rs
index 519e3bd..68516d7 100644
--- a/apkverity/src/loopdevice.rs
+++ b/apkverity/src/loopdevice.rs
@@ -85,6 +85,12 @@
     }
 }
 
+#[cfg(not(target_os = "android"))]
+const LOOP_DEV_PREFIX: &str = "/dev/loop";
+
+#[cfg(target_os = "android")]
+const LOOP_DEV_PREFIX: &str = "/dev/block/loop";
+
 fn try_attach<P: AsRef<Path>>(path: P, offset: u64, size_limit: u64) -> Result<PathBuf> {
     // Get a free loop device
     wait_for_path(LOOP_CONTROL)?;
@@ -112,12 +118,12 @@
     // happens only during test. DirectIO-on-loop-over-loop makes the outer loop device
     // unaccessible.
     #[cfg(test)]
-    if path.as_ref().to_str().unwrap().starts_with("/dev/loop") {
+    if path.as_ref().to_str().unwrap().starts_with(LOOP_DEV_PREFIX) {
         config.info.lo_flags.remove(Flag::LO_FLAGS_DIRECT_IO);
     }
 
     // Configure the loop device to attach the backing file
-    let device_path = format!("/dev/loop{}", num);
+    let device_path = format!("{}{}", LOOP_DEV_PREFIX, num);
     wait_for_path(&device_path)?;
     let device_file = OpenOptions::new()
         .read(true)