diff --git a/Android.bp b/Android.bp index d170913150c5..a99f3e50f97d 100644 --- a/Android.bp +++ b/Android.bp @@ -1294,6 +1294,18 @@ python_binary_host { ], } +python_binary_host { + name: "update_font_metadata", + defaults: ["base_default"], + main: "tools/fonts/update_font_metadata.py", + srcs: [ + "tools/fonts/update_font_metadata.py", + ], + libs: [ + "fontTools", + ], +} + filegroup { name: "framework-media-annotation-srcs", srcs: [ diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java index d2111e72f383..2029f395a352 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java @@ -169,8 +169,9 @@ public class FontManagerShellCommand extends ShellCommand { sb.append(c++); sb.append("]: lang=\""); sb.append(family.getLocaleList().toLanguageTags()); + sb.append("\""); if (family.getVariant() != FontConfig.FontFamily.VARIANT_DEFAULT) { - sb.append("\", variant="); + sb.append(", variant="); switch (family.getVariant()) { case FontConfig.FontFamily.VARIANT_COMPACT: sb.append("Compact"); diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp index 02c75edafcd5..39dc9c286d5f 100644 --- a/tests/ApkVerityTest/Android.bp +++ b/tests/ApkVerityTest/Android.bp @@ -16,6 +16,7 @@ java_test_host { name: "ApkVerityTest", srcs: ["src/**/*.java"], libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"], + static_libs: ["frameworks-base-hostutils"], test_suites: ["general-tests", "vts"], target_required: [ "block_device_writer_module", diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java index 629b6c714ae8..d0eb9befbdee 100644 --- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java +++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java @@ -21,10 +21,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.RootPermissionTest; +import com.android.fsverity.AddFsVerityCertRule; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.log.LogUtil.CLog; @@ -35,6 +35,7 @@ import com.android.tradefed.util.CommandStatus; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -85,40 +86,25 @@ public class ApkVerityTest extends BaseHostJUnit4Test { private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer"; private static final String CERT_PATH = "/data/local/tmp/ApkVerityTestCert.der"; - private static final String APK_VERITY_STANDARD_MODE = "2"; - /** Only 4K page is supported by fs-verity currently. */ private static final int FSVERITY_PAGE_SIZE = 4096; + @Rule + public final AddFsVerityCertRule mAddFsVerityCertRule = + new AddFsVerityCertRule(this, CERT_PATH); + private ITestDevice mDevice; - private String mKeyId; @Before public void setUp() throws DeviceNotAvailableException { mDevice = getDevice(); - String apkVerityMode = mDevice.getProperty("ro.apk_verity.mode"); - assumeTrue(mDevice.getLaunchApiLevel() >= 30 - || APK_VERITY_STANDARD_MODE.equals(apkVerityMode)); - - mKeyId = expectRemoteCommandToSucceed( - "mini-keyctl padd asymmetric fsv_test .fs-verity < " + CERT_PATH).trim(); - if (!mKeyId.matches("^\\d+$")) { - String keyId = mKeyId; - mKeyId = null; - fail("Key ID is not decimal: " + keyId); - } - uninstallPackage(TARGET_PACKAGE); } @After public void tearDown() throws DeviceNotAvailableException { uninstallPackage(TARGET_PACKAGE); - - if (mKeyId != null) { - expectRemoteCommandToSucceed("mini-keyctl unlink " + mKeyId + " .fs-verity"); - } } @Test diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp new file mode 100644 index 000000000000..d809fe8dad56 --- /dev/null +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -0,0 +1,30 @@ +// 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. + +java_test_host { + name: "UpdatableSystemFontTest", + srcs: ["src/**/*.java"], + libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"], + static_libs: ["frameworks-base-hostutils"], + test_suites: ["general-tests", "vts"], + data: [ + ":NotoColorEmojiTtf", + ":UpdatableSystemFontTestCertDer", + ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig", + ":UpdatableSystemFontTestNotoColorEmojiV1Ttf", + ":UpdatableSystemFontTestNotoColorEmojiV1TtfFsvSig", + ":UpdatableSystemFontTestNotoColorEmojiV2Ttf", + ":UpdatableSystemFontTestNotoColorEmojiV2TtfFsvSig", + ], +} diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml new file mode 100644 index 000000000000..efe5d703880c --- /dev/null +++ b/tests/UpdatableSystemFontTest/AndroidTest.xml @@ -0,0 +1,36 @@ + + + + diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java new file mode 100644 index 000000000000..6d161a5d7b3a --- /dev/null +++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java @@ -0,0 +1,156 @@ +/* + * 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. + */ + +package com.android.updatablesystemfont; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.platform.test.annotations.RootPermissionTest; + +import com.android.fsverity.AddFsVerityCertRule; +import com.android.tradefed.log.LogUtil.CLog; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Tests if fonts can be updated by 'cmd font'. + */ +@RootPermissionTest +@RunWith(DeviceJUnit4ClassRunner.class) +public class UpdatableSystemFontTest extends BaseHostJUnit4Test { + + private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der"; + + private static final Pattern PATTERN_FONT = Pattern.compile("path = ([^, \n]*)"); + private static final String NOTO_COLOR_EMOJI_TTF = "NotoColorEmoji.ttf"; + private static final String TEST_NOTO_COLOR_EMOJI_V1_TTF = + "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf"; + private static final String TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG = + "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig"; + private static final String TEST_NOTO_COLOR_EMOJI_V2_TTF = + "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf"; + private static final String TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG = + "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig"; + private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF = + "/data/local/tmp/NotoColorEmoji.ttf"; + private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG = + "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"; + + @Rule + public final AddFsVerityCertRule mAddFsverityCertRule = + new AddFsVerityCertRule(this, CERT_PATH); + + @Before + public void setUp() throws Exception { + expectRemoteCommandToSucceed("cmd font clear"); + } + + @After + public void tearDown() throws Exception { + expectRemoteCommandToSucceed("cmd font clear"); + } + + @Test + public void updateFont() throws Exception { + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG)); + String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(fontPath).startsWith("/data/fonts/files/"); + } + + @Test + public void updateFont_twice() throws Exception { + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG)); + String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V2_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG)); + String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(fontPath2).startsWith("/data/fonts/files/"); + assertThat(fontPath2).isNotEqualTo(fontPath); + } + + @Test + public void updatedFont_dataFileIsImmutableAndReadable() throws Exception { + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG)); + String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(fontPath).startsWith("/data"); + + expectRemoteCommandToFail("echo -n '' >> " + fontPath); + expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null"); + } + + @Test + public void updateFont_invalidCert() throws Exception { + expectRemoteCommandToFail(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG)); + } + + @Test + public void updateFont_downgradeFromSystem() throws Exception { + expectRemoteCommandToFail(String.format("cmd font update %s %s", + ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG)); + } + + @Test + public void updateFont_downgradeFromData() throws Exception { + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V2_TTF, TEST_NOTO_COLOR_EMOJI_V2_TTF_FSV_SIG)); + expectRemoteCommandToFail(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG)); + } + + private String getFontPath(String fontFileName) throws Exception { + // TODO: add a dedicated command for testing. + String lines = expectRemoteCommandToSucceed("cmd font dump"); + for (String line : lines.split("\n")) { + Matcher m = PATTERN_FONT.matcher(line); + if (m.find() && m.group(1).endsWith(fontFileName)) { + return m.group(1); + } + } + CLog.e("Font not found: " + fontFileName); + return null; + } + + private String expectRemoteCommandToSucceed(String cmd) throws Exception { + CommandResult result = getDevice().executeShellV2Command(cmd); + assertWithMessage("`" + cmd + "` failed: " + result.getStderr()) + .that(result.getStatus()) + .isEqualTo(CommandStatus.SUCCESS); + return result.getStdout(); + } + + private void expectRemoteCommandToFail(String cmd) throws Exception { + CommandResult result = getDevice().executeShellV2Command(cmd); + assertWithMessage("Unexpected success from `" + cmd + "`: " + result.getStderr()) + .that(result.getStatus()) + .isNotEqualTo(CommandStatus.SUCCESS); + } +} diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp new file mode 100644 index 000000000000..1296699e3c9f --- /dev/null +++ b/tests/UpdatableSystemFontTest/testdata/Android.bp @@ -0,0 +1,82 @@ +// 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. + +filegroup { + name: "UpdatableSystemFontTestKeyPem", + srcs: ["UpdatableSystemFontTestKey.pem"], +} + +filegroup { + name: "UpdatableSystemFontTestCertPem", + srcs: ["UpdatableSystemFontTestCert.pem"], +} + +filegroup { + name: "UpdatableSystemFontTestCertDer", + srcs: ["UpdatableSystemFontTestCert.der"], +} + +genrule_defaults { + name: "updatable_system_font_increment_font_revision_default", + tools: ["update_font_metadata"], + cmd: "$(location update_font_metadata) " + + "--input=$(in) " + + "--output=$(out) " + + "--revision=+1", +} + +genrule { + name: "UpdatableSystemFontTestNotoColorEmojiV1Ttf", + defaults: ["updatable_system_font_increment_font_revision_default"], + srcs: [":NotoColorEmojiTtf"], + out: ["UpdatableSystemFontTestNotoColorEmojiV1.ttf"], +} + +genrule { + name: "UpdatableSystemFontTestNotoColorEmojiV2Ttf", + defaults: ["updatable_system_font_increment_font_revision_default"], + srcs: [":UpdatableSystemFontTestNotoColorEmojiV1Ttf"], + out: ["UpdatableSystemFontTestNotoColorEmojiV2.ttf"], +} + +genrule_defaults { + name: "updatable_system_font_sig_gen_default", + tools: ["fsverity"], + tool_files: [":UpdatableSystemFontTestKeyPem", ":UpdatableSystemFontTestCertPem"], + cmd: "$(location fsverity) sign $(in) $(out) " + + "--key=$(location :UpdatableSystemFontTestKeyPem) " + + "--cert=$(location :UpdatableSystemFontTestCertPem) " + + "> /dev/null", +} + +genrule { + name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig", + defaults: ["updatable_system_font_sig_gen_default"], + srcs: [":NotoColorEmojiTtf"], + out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"], +} + +genrule { + name: "UpdatableSystemFontTestNotoColorEmojiV1TtfFsvSig", + defaults: ["updatable_system_font_sig_gen_default"], + srcs: [":UpdatableSystemFontTestNotoColorEmojiV1Ttf"], + out: ["UpdatableSystemFontTestNotoColorEmojiV1.ttf.fsv_sig"], +} + +genrule { + name: "UpdatableSystemFontTestNotoColorEmojiV2TtfFsvSig", + defaults: ["updatable_system_font_sig_gen_default"], + srcs: [":UpdatableSystemFontTestNotoColorEmojiV2Ttf"], + out: ["UpdatableSystemFontTestNotoColorEmojiV2.ttf.fsv_sig"], +} diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der new file mode 100644 index 000000000000..f7aa15fdef68 Binary files /dev/null and b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.der differ diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem new file mode 100644 index 000000000000..0cd1f666f9be --- /dev/null +++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestCert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFOTCCAyGgAwIBAgIUFaI1D5NtwkCVM3G4bFZ6sQSb598wDQYJKoZIhvcNAQEL +BQAwLDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdBbmRyb2lk +MB4XDTIxMDIwMTA3MzAyNFoXDTIxMDMwMzA3MzAyNFowLDELMAkGA1UEBhMCVVMx +CzAJBgNVBAgMAkNBMRAwDgYDVQQKDAdBbmRyb2lkMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0U1zptc41E65ooeBPD33Mjgp6cYPydyj2Acq80Xy7lP7 +d6/2t6w7nNNl2x3n8dAhOl3de3IxTp6JI2SdoRb7obfcp+hWJoo/cxnHpr3q/u4R +KED0rmWaOVHpGbajSTFZgN+cTbTKJbgtXm/H65x1QfO18ep/vj5fRiu1xPpJqDv/ +xuvuko2U3eC2+NayxzCWXVFrKPLx8GvzSQ3Utaug17vs7/5GqkRJgq3lk4DvmjNA +vY8YA4RAkII1sSaceAWFEG6ztENLu2kjcxAI9qHxxBwQZit/NtFVlFGqSN3MEYjS +M9Fz04RsUxF672QJpAgwCJDZ41rdB3hkHvOUK9PcepBsHdZq9cQ+E64+TX+jsJLu +VouViKlYr6WYjvhfqZeRhwbj7CoEZ2DyEZKrl27fgWaidUT5LGEQLVxg90ymbimI +6UwXRUwmRBQJBdRO4RGvngtqxRuamyjAKDDHx5YccXCX4FWLUypyQlz0asojvbJZ +Og7DFa1qsRdGrGIRoQJ8pYnAjBJfSudr1l0mR7fZSfZc0W9ZmuROWx9Ip7aJWQnQ +8JLtbNPuFLD2qbmg9Y1lcXJp1FvI9FcM8JsBqZNEANQwwsdTaa8gw+3W6J2SXKQP +H+yZI/fJWWWRFADtqmpxtvXK9K+Cy1HQmg7D0IIxVPp8rrbz6TGrg+R3/N9FBWMC +AwEAAaNTMFEwHQYDVR0OBBYEFDS48o2UAstoOyLMcgamHKrZdl3cMB8GA1UdIwQY +MBaAFDS48o2UAstoOyLMcgamHKrZdl3cMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBAF7taBYAe20tWZu0pY9d4Z8il4LoJcRrKF4YiA02UizErgCF +h4iECy6+pcu7DJUfvCh3dCWE7CDG+OnfUWTwEHVG9n8XI/ydetBUG76PZwTadI7B +gzJ1y7/vWqJo5U6ki+sXNmq3hkgNsNZgza3LpdovkWJYeRdffM6m/bimzwYx9id8 +5mKw2PcbVZcb25r+0dCoLVJsqqCoRjdYUy/MKPutWG2bPzmaIv8KsKFN+mzlwhJH +lpJ/LR+3NoaHrOCFG7CW/2Ihe501vmdQ2m/VKosyk0igw8WmTsY6xMbw2t77yKkD +hnJr1NbhKeEV9gAB2BFX8nRWI7NTgp8fG78YLVz1UcbIHmYLgFoc3ezyma+CoR86 +ER20lKd4+TNnz4RtaPdZlBa0Ba3bsMtEneqlrHvcPrZ5tgGsQR9+cy3ZtTZ/LUQX ++Xuj/EoJXuuB3hkhg52zawN5n7WUe8efWHcv1jHqeIj0phcgbZ6u4fFBPsYjzDKe +VuYHXglNOchmoBQwEaJI/TCiEgI8dcSJXSquLAXrtznVnxzT46ZMEt5LaW1/1NLx +q//yoPdolCI0lpunh5jvIZJpUl5XMjxVSyaveQDNVqJkITWzWqIxAT5yTLtkCNlW +c1XyzeHkpMItiJtBruExmnaTmNjlVKsXP8wQFOYbDGgXY5iHIMbgovptRyH/ +-----END CERTIFICATE----- diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem new file mode 100644 index 000000000000..09bb1040f112 --- /dev/null +++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTestKey.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDRTXOm1zjUTrmi +h4E8PfcyOCnpxg/J3KPYByrzRfLuU/t3r/a3rDuc02XbHefx0CE6Xd17cjFOnokj +ZJ2hFvuht9yn6FYmij9zGcemver+7hEoQPSuZZo5UekZtqNJMVmA35xNtMoluC1e +b8frnHVB87Xx6n++Pl9GK7XE+kmoO//G6+6SjZTd4Lb41rLHMJZdUWso8vHwa/NJ +DdS1q6DXu+zv/kaqREmCreWTgO+aM0C9jxgDhECQgjWxJpx4BYUQbrO0Q0u7aSNz +EAj2ofHEHBBmK3820VWUUapI3cwRiNIz0XPThGxTEXrvZAmkCDAIkNnjWt0HeGQe +85Qr09x6kGwd1mr1xD4Trj5Nf6Owku5Wi5WIqVivpZiO+F+pl5GHBuPsKgRnYPIR +kquXbt+BZqJ1RPksYRAtXGD3TKZuKYjpTBdFTCZEFAkF1E7hEa+eC2rFG5qbKMAo +MMfHlhxxcJfgVYtTKnJCXPRqyiO9slk6DsMVrWqxF0asYhGhAnylicCMEl9K52vW +XSZHt9lJ9lzRb1ma5E5bH0intolZCdDwku1s0+4UsPapuaD1jWVxcmnUW8j0Vwzw +mwGpk0QA1DDCx1NpryDD7dbonZJcpA8f7Jkj98lZZZEUAO2qanG29cr0r4LLUdCa +DsPQgjFU+nyutvPpMauD5Hf830UFYwIDAQABAoICAQCTVTcFCdl6MdSg4UwK0P/S +fRCb/A0fJs67Agis6N9h/wI0NUyx7G6mLXU0si+U29KYGH0RKcgltJmKrYf8XoZR +R3DvTTBfvs99QXd2G5hxTboMIPVcUi8nDE7PB+6XVkLP4hhP5uSpeqWNJZiQdTlh +bKH2IgE8NQGyDpDMkPcKkvmw2GG/DiTtrwJ91fxRFRWzqN2LHMFMYWEHWtIR9Der +xSC7q72om5s3fxvtIkUHwe5fwXvA9fbRAqezBR/9qL0LXTHowbpsuUz38SCuJD9g +sfSlRxcsyly4pGf/FQpSiYKWcWlcSopKSzLDkyLqMc1GKlkGnu6aFJg95W63D1LS +OaOXuYShHxLkqyhT8uQGRqDCu3E2ivb6fMxAPzJxxs3JrZvumNsqyxbp+HVF0idj +NijMN/8Kb4KmNHG9I3SHG61tQFYDtxoMMNiHzq3fafBJnVcf6iThQdE5pGLN2OdF +3rcSTeHI2HxhTrXtuiHmWXNk9aZ2TOhrssNZZjDkFL/KYh6G8guy+tn66YWy77VC +id+6PBzYXTXsUauo4NWW1rLUfzT/y93IwVGpoXs7GHUUduZ6Q3PxsMTMF9IjBvqR +JvfP84CUfGXoebVJmWyGhtW6N5ParvQxbonDitA0TPZcRyX3N8yqIGO/mb8MWA9M +7s/xMZuksOpw5LSCoTAW6QKCAQEA9Z1WM/5XT+5HkwPBvS5aRz0SqMqPxAkZ0dNz +O06zpXv6e2IPy40UzFWCIkyq3vWKQ5bqU1fnejUdmjvtnP+KhH6fxnQCgiunnrDq +j1Sk2Y4gb1KyZY/C8IejOexM2qX7sfDTLI8XEvxJxVFCNmvYfnv4AX5QD8VOsjrg +bvodLAgDSo2FVDP+mkpW7zAIoRV02l6QdZA+YcG940eqxB9sPR3/1KUHe9wUTTbJ +FV5ahEPuXCyvRJkZ/rD5CPPZoQHfjKHxDlu7yfoatzCSYj10R+RInfqOFGVY4D/C +2csXjymwTN4CFUnJcP410YhPFn5ekmc/E3xqPKgIDQhQ2+oivwKCAQEA2icN1iEo +YuBJwB3pX3jrwk+1bpUXASueWhyAhSeNMrTrJ/lSgEAy9FBxJNnK1PjkABRnFhS+ +uxbC2hQdfAkNDS21PQOk6hOhebUVuBdfmKY1CL+P9y4Af0fjV1rgRGHihnZB5lKU +1R/wFb8c4QgPwduiqDoZ5QP3dgxpZ4R3SCzuoyVelUSlgyWPoslg+yPddcnV8bTf +BUlEOOyXgVudkSFlRpWZ+/ZAkLTj8rnrtKp8/+GtTQvJvPJHPfuSsDhI+EQVWqab +HelMhxvIi9xOyN/OCc3Ex6JlEVa+X8EyNhQsV2sAKnld92hdEozW2uxRniIkxvL4 +CuBp/p3fWxEaXQKCAQEAw2j7RYCMnNZZ8Zhiko4HW3g2mT4XpYMMHMlbe4sBGJ8L +yRBaurqzGmLJl1ph8+NsrpuqMMbWLn+F3sjhIjCZVxKbMbvopwHuaS4eYAya30/Z +dFhaAL2g/dccQSBEgQzftFGC4YeydvNsCeW9hSjGZNNinGWPcwyqsNhw6Tpq7TYu +0CjKNBTt8nlEsyYHJ4m3n2jvC+nIB+Spm+LP9Rt+9R0iBl+KFbwiFtCIqUyZPXQC +dylCBJS+fsj0SXAg7J1d6ziIXcEUJfyrNqYZQLneAriYIcBPO+DqFfgEoVyYkNk9 +H9rd02wSLajCzsLhEWdW/KnSIEGzEDErvpqoIl8kZwKCAQEAjNO3T+sZyjKmCXqF +xBcogsi4BAoEzsGcuOk7Yjn1Ia2/PI/r3VUUT7l6QOLD2JZPgWmqXovH0LjR0rw3 +iHHDViWSoS+wD1fa3tmyiqO0F7P7+ojHZDbzJTeAIE1PB3X1KP5AbnITGD5E25UD +DJYKrgeeSmEvhDL6Vd+PT78ozZQL/Y/LLishebcOsXS0wYsWlMpV7XHoot34R5Mb +/urond7kJRvASvJeHcxYdsHk0j1Y8kp6eIlKk0oICZBU0qOTH4m8C0gQTM/lkjay +UO9IgM5RkOyfwowoGHhZ7zClvFlrgodVlRXCPkvGAYqfzLXPvnimKzSAQW07n53E +qWIyFQKCAQAWRNG6hPCOzkNxMJ/RK7dwxZW6b4a1L3PMXTT/xrBKRIS1WNjbpkYO +/FLIufOqJT6FQN2obM5uso3TI+R7MwH8DnTSnDDy0Hvs3CdHdtn2tapZOViF/UVv +uCQa+/jMVKFCZ8k7pPFMIG6tB6WBA5MmJrW+8s0ouxLbRF5rZyzqOeyPdVBYYYDb +68nGNA6GAtTQs9h7xV2tsQ1bXNP+6gqG3BgZYo+76xKITddT6s9aaC++LVBnPOdq +LHV7gUvoBkVLjIp4L3Fb/DGMCcOVMCxmFlRBn+RBlV7slehvgq+Ywz2GHWLr+O/z +V2NAtwvCfZE2Do/4f2mpHnamhS6AvrDe +-----END PRIVATE KEY----- diff --git a/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java b/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java new file mode 100644 index 000000000000..5ab4dc60e2ac --- /dev/null +++ b/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package com.android.fsverity; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assume.assumeTrue; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.log.LogUtil; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; + +import org.junit.rules.ExternalResource; + +public final class AddFsVerityCertRule extends ExternalResource { + + private static final String APK_VERITY_STANDARD_MODE = "2"; + + private final BaseHostJUnit4Test mHost; + private final String mCertPath; + private String mKeyId; + + public AddFsVerityCertRule(BaseHostJUnit4Test host, String certPath) { + mHost = host; + mCertPath = certPath; + } + + @Override + protected void before() throws Throwable { + ITestDevice device = mHost.getDevice(); + String apkVerityMode = device.getProperty("ro.apk_verity.mode"); + assumeTrue(device.getLaunchApiLevel() >= 30 + || APK_VERITY_STANDARD_MODE.equals(apkVerityMode)); + + String keyId = executeCommand( + "mini-keyctl padd asymmetric fsv_test .fs-verity < " + mCertPath).trim(); + assertThat(keyId).matches("^\\d+$"); + mKeyId = keyId; + } + + @Override + protected void after() { + if (mKeyId == null) return; + try { + executeCommand("mini-keyctl unlink " + mKeyId + " .fs-verity"); + } catch (DeviceNotAvailableException e) { + LogUtil.CLog.e(e); + } + mKeyId = null; + } + + private String executeCommand(String cmd) throws DeviceNotAvailableException { + CommandResult result = mHost.getDevice().executeShellV2Command(cmd); + assertWithMessage("`" + cmd + "` failed: " + result.getStderr()) + .that(result.getStatus()) + .isEqualTo(CommandStatus.SUCCESS); + return result.getStdout(); + } +} diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py new file mode 100755 index 000000000000..c07a98a1e3d2 --- /dev/null +++ b/tools/fonts/update_font_metadata.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +import argparse + +from fontTools import ttLib + + +def update_font_revision(font, revisionSpec): + if revisionSpec.startswith('+'): + font['head'].fontRevision += float(revisionSpec[1:]) + else: + font['head'].fontRevision = float(revisionSpec) + + +def main(): + args_parser = argparse.ArgumentParser(description='Update font file metadata') + args_parser.add_argument('--input', help='Input otf/ttf font file.') + args_parser.add_argument('--output', help='Output file for updated font file.') + args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision') + args = args_parser.parse_args() + + font = ttLib.TTFont(args.input) + update_font_revision(font, args.revision) + font.save(args.output) + +if __name__ == "__main__": + main()