diff options
26 files changed, 1637 insertions, 369 deletions
diff --git a/Android.bp b/Android.bp index 8e09157bb461..67902b310481 100644 --- a/Android.bp +++ b/Android.bp @@ -106,6 +106,7 @@ filegroup { ":android.security.maintenance-java-source", ":android.security.metrics-java-source", ":android.system.keystore2-V3-java-source", + ":android.hardware.cas-V1-java-source", ":credstore_aidl", ":dumpstate_aidl", ":framework_native_aidl", @@ -196,6 +197,7 @@ java_library { "updatable-driver-protos", "ota_metadata_proto_java", "android.hidl.base-V1.0-java", + "android.hardware.cas-V1-java", // AIDL "android.hardware.cas-V1.0-java", "android.hardware.cas-V1.1-java", "android.hardware.cas-V1.2-java", diff --git a/StubLibraries.bp b/StubLibraries.bp index 272b4f6e36e6..48c44c967ef0 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -378,6 +378,67 @@ java_library { }, } +java_library { + name: "android_stubs_private_jar", + defaults: ["android.jar_defaults"], + visibility: [ + "//visibility:override", + "//visibility:private", + ], + static_libs: [ + "stable.core.platform.api.stubs", + "core-lambda-stubs-for-system-modules", + "core-generated-annotation-stubs", + "framework", + "ext", + "framework-res-package-jar", + // The order of this matters, it has to be last to provide a + // package-private androidx.annotation.RecentlyNonNull without + // overriding the public android.annotation.Nullable in framework.jar + // with its own package-private android.annotation.Nullable. + "private-stub-annotations-jar", + ], +} + +java_genrule { + name: "android_stubs_private_hjar", + visibility: ["//visibility:private"], + srcs: [":android_stubs_private_jar{.hjar}"], + out: ["android_stubs_private.jar"], + cmd: "cp $(in) $(out)", +} + +java_library { + name: "android_stubs_private", + defaults: ["android_stubs_dists_default"], + visibility: ["//visibility:private"], + sdk_version: "none", + system_modules: "none", + static_libs: ["android_stubs_private_hjar"], + dist: { + dir: "apistubs/android/private", + }, +} + +java_genrule { + name: "android_stubs_private_framework_aidl", + visibility: ["//visibility:private"], + tools: ["sdkparcelables"], + srcs: [":android_stubs_private"], + out: ["framework.aidl"], + cmd: "rm -f $(genDir)/framework.aidl.merged && " + + "for i in $(in); do " + + " rm -f $(genDir)/framework.aidl.tmp && " + + " $(location sdkparcelables) $$i $(genDir)/framework.aidl.tmp && " + + " cat $(genDir)/framework.aidl.tmp >> $(genDir)/framework.aidl.merged; " + + "done && " + + "sort -u $(genDir)/framework.aidl.merged > $(out)", + dist: { + targets: ["sdk"], + dir: "apistubs/android/private", + }, +} + //////////////////////////////////////////////////////////////////////// // api-versions.xml generation, for public and system. This API database // also contains the android.test.* APIs. diff --git a/core/api/current.txt b/core/api/current.txt index ff9c7b130e95..a0c132f980dc 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -17088,8 +17088,8 @@ package android.hardware { method public boolean flush(android.hardware.SensorEventListener); method public static float getAltitude(float, float); method public static void getAngleChange(float[], float[], float[]); - method public android.hardware.Sensor getDefaultSensor(int); - method public android.hardware.Sensor getDefaultSensor(int, boolean); + method @Nullable public android.hardware.Sensor getDefaultSensor(int); + method @Nullable public android.hardware.Sensor getDefaultSensor(int, boolean); method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int); method public static float getInclination(float[]); method public static float[] getOrientation(float[], float[]); @@ -21142,6 +21142,7 @@ package android.media { field public static final int PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED = 0; // 0x0 field public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = 1; // 0x1 field public static final int SCRAMBLING_MODE_AES128 = 9; // 0x9 + field public static final int SCRAMBLING_MODE_AES_CBC = 14; // 0xe field public static final int SCRAMBLING_MODE_AES_ECB = 10; // 0xa field public static final int SCRAMBLING_MODE_AES_SCTE52 = 11; // 0xb field public static final int SCRAMBLING_MODE_DVB_CISSA_V1 = 6; // 0x6 diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index dec424c3ad7a..6d8c4a93b44e 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -16,6 +16,7 @@ package android.hardware; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; @@ -496,7 +497,7 @@ public abstract class SensorManager { * @see #getSensorList(int) * @see Sensor */ - public Sensor getDefaultSensor(int type) { + public @Nullable Sensor getDefaultSensor(int type) { // TODO: need to be smarter, for now, just return the 1st sensor List<Sensor> l = getSensorList(type); boolean wakeUpSensor = false; @@ -544,7 +545,7 @@ public abstract class SensorManager { * and the application has the necessary permissions, or null otherwise. * @see Sensor#isWakeUpSensor() */ - public Sensor getDefaultSensor(int type, boolean wakeUp) { + public @Nullable Sensor getDefaultSensor(int type, boolean wakeUp) { List<Sensor> l = getSensorList(type); for (Sensor sensor : l) { if (sensor.isWakeUpSensor() == wakeUp) { diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 468e6041eb73..5abe677d0e1f 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -88,8 +88,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -1027,6 +1027,19 @@ public class CameraMetadataNative implements Parcelable { return fixedFaceRectangles; } + private boolean setLensShadingMap(LensShadingMap lensShadingMap) { + if (lensShadingMap == null) { + return false; + } + float[] lsmArray = new float[lensShadingMap.getGainFactorCount()]; + lensShadingMap.copyGainFactors(lsmArray, 0); + setBase(CaptureResult.STATISTICS_LENS_SHADING_MAP, lsmArray); + + Size s = new Size(lensShadingMap.getRowCount(), lensShadingMap.getColumnCount()); + setBase(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, s); + return true; + } + private LensShadingMap getLensShadingMap() { float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP); Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE); @@ -1851,6 +1864,13 @@ public class CameraMetadataNative implements Parcelable { metadata.setAERegions(value); } }); + sSetCommandMap.put(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(), + new SetCommand() { + @Override + public <T> void setValue(CameraMetadataNative metadata, T value) { + metadata.setLensShadingMap((LensShadingMap) value); + } + }); } private boolean setAvailableFormats(int[] value) { diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index ac46be76bac4..0c9eaa41ade0 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4815,5 +4815,12 @@ public final class Telephony { * @hide */ public static final String COLUMN_USAGE_SETTING = "usage_setting"; + + /** + * TelephonyProvider column name for user handle associated with this sim. + * + * @hide + */ + public static final String COLUMN_USER_HANDLE = "user_handle"; } } diff --git a/core/tests/coretests/src/android/hardware/camera2/OWNERS b/core/tests/coretests/src/android/hardware/camera2/OWNERS new file mode 100644 index 000000000000..f48a95c5b3a3 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/camera2/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/av:/camera/OWNERS diff --git a/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java b/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java new file mode 100644 index 000000000000..a38c040c7746 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java @@ -0,0 +1,55 @@ +/* + * 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 android.hardware.camera2.impl; + +import static com.google.common.truth.Truth.assertThat; + +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.params.LensShadingMap; +import android.util.Size; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; + +@SmallTest +@RunWith(JUnit4.class) +/** Tests for {@link CameraMetadataNative} class. */ +public class CaptureMetadataNativeTest { + + @Test + public void setLensShadingMap() { + final Size s = new Size(10, 10); + // 4 x rows x columns + final float[] elements = new float[400]; + Arrays.fill(elements, 42); + + final LensShadingMap lensShadingMap = + new LensShadingMap(elements, s.getHeight(), s.getWidth()); + CameraMetadataNative captureResults = new CameraMetadataNative(); + captureResults.set(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP, lensShadingMap); + + final LensShadingMap output = + captureResults.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP); + + assertThat(output).isEqualTo(lensShadingMap); + } +} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index c0220879094a..2800e3148988 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -634,7 +634,7 @@ cc_library_static { cc_defaults { name: "hwui_test_defaults", defaults: ["hwui_defaults"], - test_suites: ["device-tests"], + test_suites: ["general-tests"], header_libs: ["libandroid_headers_private"], target: { android: { diff --git a/libs/hwui/AndroidTest.xml b/libs/hwui/AndroidTest.xml index 381fb9f6c7bf..911315f81a8a 100644 --- a/libs/hwui/AndroidTest.xml +++ b/libs/hwui/AndroidTest.xml @@ -16,22 +16,22 @@ <configuration description="Config for hwuimicro"> <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> - <option name="push" value="hwui_unit_tests->/data/nativetest/hwui_unit_tests" /> - <option name="push" value="hwuimicro->/data/benchmarktest/hwuimicro" /> - <option name="push" value="hwuimacro->/data/benchmarktest/hwuimacro" /> + <option name="push" value="hwui_unit_tests->/data/local/tmp/nativetest/hwui_unit_tests" /> + <option name="push" value="hwuimicro->/data/local/tmp/benchmarktest/hwuimicro" /> + <option name="push" value="hwuimacro->/data/local/tmp/benchmarktest/hwuimacro" /> </target_preparer> <option name="test-suite-tag" value="apct" /> <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/nativetest" /> + <option name="native-test-device-path" value="/data/local/tmp/nativetest" /> <option name="module-name" value="hwui_unit_tests" /> </test> <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" > - <option name="native-benchmark-device-path" value="/data/benchmarktest" /> + <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" /> <option name="benchmark-module-name" value="hwuimicro" /> <option name="file-exclusion-filter-regex" value=".*\.config$" /> </test> <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" > - <option name="native-benchmark-device-path" value="/data/benchmarktest" /> + <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" /> <option name="benchmark-module-name" value="hwuimacro" /> <option name="file-exclusion-filter-regex" value=".*\.config$" /> </test> diff --git a/libs/hwui/TEST_MAPPING b/libs/hwui/TEST_MAPPING index b1719a979ce5..03682e82e28d 100644 --- a/libs/hwui/TEST_MAPPING +++ b/libs/hwui/TEST_MAPPING @@ -5,6 +5,9 @@ }, { "name": "CtsAccelerationTestCases" + }, + { + "name": "hwui_unit_tests" } ], "imports": [ diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index edd3e4e4f4d4..fc84abb1d605 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -32,7 +32,8 @@ static size_t getCacheUsage(GrDirectContext* grContext) { return cacheUsage; } -RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { +// TOOD(258700630): fix this test and re-enable +RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) { int32_t width = DeviceInfo::get()->getWidth(); int32_t height = DeviceInfo::get()->getHeight(); GrDirectContext* grContext = renderThread.getGrContext(); diff --git a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp index 098b4ccea8cf..c2d23e6d1101 100644 --- a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp +++ b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp @@ -14,17 +14,18 @@ * limitations under the License. */ +#include <android-base/macros.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> - -#include "protos/graphicsstats.pb.h" -#include "service/GraphicsStatsService.h" - #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include "protos/graphicsstats.pb.h" +#include "service/GraphicsStatsService.h" + using namespace android; using namespace android::uirenderer; @@ -49,12 +50,14 @@ std::string findRootPath() { // No code left untested TEST(GraphicsStats, findRootPath) { -#ifdef __LP64__ - std::string expected = "/data/nativetest64/hwui_unit_tests"; -#else - std::string expected = "/data/nativetest/hwui_unit_tests"; -#endif - EXPECT_EQ(expected, findRootPath()); + // Different tools/infrastructure seem to push this to different locations. It shouldn't really + // matter where the binary is, so add new locations here as needed. This test still seems good + // as it's nice to understand the possibility space, and ensure findRootPath continues working + // as expected. + std::string acceptableLocations[] = {"/data/nativetest/hwui_unit_tests", + "/data/nativetest64/hwui_unit_tests", + "/data/local/tmp/nativetest/hwui_unit_tests/" ABI_STRING}; + EXPECT_THAT(acceptableLocations, ::testing::Contains(findRootPath())); } TEST(GraphicsStats, saveLoad) { diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index ff1714da2008..a996700eed2a 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -60,6 +60,7 @@ cc_defaults { ":libincident_aidl", "src/IncidentReportArgs.cpp", ], + min_sdk_version: "29", } cc_library_shared { diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index 582a28ee278e..015602e95533 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -21,11 +21,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.content.Context; +import android.hardware.cas.AidlCasPluginDescriptor; +import android.hardware.cas.ICas; +import android.hardware.cas.ICasListener; +import android.hardware.cas.IMediaCasService; +import android.hardware.cas.Status; import android.hardware.cas.V1_0.HidlCasPluginDescriptor; -import android.hardware.cas.V1_0.ICas; -import android.hardware.cas.V1_0.IMediaCasService; -import android.hardware.cas.V1_2.ICasListener; -import android.hardware.cas.V1_2.Status; import android.media.MediaCasException.*; import android.media.tv.TvInputService.PriorityHintUseCaseType; import android.media.tv.tunerresourcemanager.CasSessionRequest; @@ -39,6 +40,7 @@ import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import android.util.Singleton; @@ -47,6 +49,7 @@ import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -114,9 +117,10 @@ import java.util.Objects; */ public final class MediaCas implements AutoCloseable { private static final String TAG = "MediaCas"; - private ICas mICas; - private android.hardware.cas.V1_1.ICas mICasV11; - private android.hardware.cas.V1_2.ICas mICasV12; + private ICas mICas = null; + private android.hardware.cas.V1_0.ICas mICasHidl = null; + private android.hardware.cas.V1_1.ICas mICasHidl11 = null; + private android.hardware.cas.V1_2.ICas mICasHidl12 = null; private EventListener mListener; private HandlerThread mHandlerThread; private EventHandler mEventHandler; @@ -133,88 +137,84 @@ public final class MediaCas implements AutoCloseable { * * @hide */ - @IntDef(prefix = "SCRAMBLING_MODE_", - value = {SCRAMBLING_MODE_RESERVED, SCRAMBLING_MODE_DVB_CSA1, SCRAMBLING_MODE_DVB_CSA2, - SCRAMBLING_MODE_DVB_CSA3_STANDARD, - SCRAMBLING_MODE_DVB_CSA3_MINIMAL, SCRAMBLING_MODE_DVB_CSA3_ENHANCE, - SCRAMBLING_MODE_DVB_CISSA_V1, SCRAMBLING_MODE_DVB_IDSA, - SCRAMBLING_MODE_MULTI2, SCRAMBLING_MODE_AES128, SCRAMBLING_MODE_AES_ECB, - SCRAMBLING_MODE_AES_SCTE52, SCRAMBLING_MODE_TDES_ECB, SCRAMBLING_MODE_TDES_SCTE52}) + @IntDef( + prefix = "SCRAMBLING_MODE_", + value = { + SCRAMBLING_MODE_RESERVED, + SCRAMBLING_MODE_DVB_CSA1, + SCRAMBLING_MODE_DVB_CSA2, + SCRAMBLING_MODE_DVB_CSA3_STANDARD, + SCRAMBLING_MODE_DVB_CSA3_MINIMAL, + SCRAMBLING_MODE_DVB_CSA3_ENHANCE, + SCRAMBLING_MODE_DVB_CISSA_V1, + SCRAMBLING_MODE_DVB_IDSA, + SCRAMBLING_MODE_MULTI2, + SCRAMBLING_MODE_AES128, + SCRAMBLING_MODE_AES_CBC, + SCRAMBLING_MODE_AES_ECB, + SCRAMBLING_MODE_AES_SCTE52, + SCRAMBLING_MODE_TDES_ECB, + SCRAMBLING_MODE_TDES_SCTE52 + }) @Retention(RetentionPolicy.SOURCE) public @interface ScramblingMode {} - /** - * DVB (Digital Video Broadcasting) reserved mode. - */ - public static final int SCRAMBLING_MODE_RESERVED = - android.hardware.cas.V1_2.ScramblingMode.RESERVED; - /** - * DVB (Digital Video Broadcasting) Common Scrambling Algorithm (CSA) 1. - */ - public static final int SCRAMBLING_MODE_DVB_CSA1 = - android.hardware.cas.V1_2.ScramblingMode.DVB_CSA1; - /** - * DVB CSA 2. - */ - public static final int SCRAMBLING_MODE_DVB_CSA2 = - android.hardware.cas.V1_2.ScramblingMode.DVB_CSA2; - /** - * DVB CSA 3 in standard mode. - */ + /** DVB (Digital Video Broadcasting) reserved mode. */ + public static final int SCRAMBLING_MODE_RESERVED = android.hardware.cas.ScramblingMode.RESERVED; + + /** DVB (Digital Video Broadcasting) Common Scrambling Algorithm (CSA) 1. */ + public static final int SCRAMBLING_MODE_DVB_CSA1 = android.hardware.cas.ScramblingMode.DVB_CSA1; + + /** DVB CSA 2. */ + public static final int SCRAMBLING_MODE_DVB_CSA2 = android.hardware.cas.ScramblingMode.DVB_CSA2; + + /** DVB CSA 3 in standard mode. */ public static final int SCRAMBLING_MODE_DVB_CSA3_STANDARD = - android.hardware.cas.V1_2.ScramblingMode.DVB_CSA3_STANDARD; - /** - * DVB CSA 3 in minimally enhanced mode. - */ + android.hardware.cas.ScramblingMode.DVB_CSA3_STANDARD; + + /** DVB CSA 3 in minimally enhanced mode. */ public static final int SCRAMBLING_MODE_DVB_CSA3_MINIMAL = - android.hardware.cas.V1_2.ScramblingMode.DVB_CSA3_MINIMAL; - /** - * DVB CSA 3 in fully enhanced mode. - */ + android.hardware.cas.ScramblingMode.DVB_CSA3_MINIMAL; + + /** DVB CSA 3 in fully enhanced mode. */ public static final int SCRAMBLING_MODE_DVB_CSA3_ENHANCE = - android.hardware.cas.V1_2.ScramblingMode.DVB_CSA3_ENHANCE; - /** - * DVB Common IPTV Software-oriented Scrambling Algorithm (CISSA) Version 1. - */ + android.hardware.cas.ScramblingMode.DVB_CSA3_ENHANCE; + + /** DVB Common IPTV Software-oriented Scrambling Algorithm (CISSA) Version 1. */ public static final int SCRAMBLING_MODE_DVB_CISSA_V1 = - android.hardware.cas.V1_2.ScramblingMode.DVB_CISSA_V1; - /** - * ATIS-0800006 IIF Default Scrambling Algorithm (IDSA). - */ - public static final int SCRAMBLING_MODE_DVB_IDSA = - android.hardware.cas.V1_2.ScramblingMode.DVB_IDSA; - /** - * A symmetric key algorithm. - */ - public static final int SCRAMBLING_MODE_MULTI2 = - android.hardware.cas.V1_2.ScramblingMode.MULTI2; - /** - * Advanced Encryption System (AES) 128-bit Encryption mode. - */ - public static final int SCRAMBLING_MODE_AES128 = - android.hardware.cas.V1_2.ScramblingMode.AES128; - /** - * Advanced Encryption System (AES) Electronic Code Book (ECB) mode. - */ - public static final int SCRAMBLING_MODE_AES_ECB = - android.hardware.cas.V1_2.ScramblingMode.AES_ECB; + android.hardware.cas.ScramblingMode.DVB_CISSA_V1; + + /** ATIS-0800006 IIF Default Scrambling Algorithm (IDSA). */ + public static final int SCRAMBLING_MODE_DVB_IDSA = android.hardware.cas.ScramblingMode.DVB_IDSA; + + /** A symmetric key algorithm. */ + public static final int SCRAMBLING_MODE_MULTI2 = android.hardware.cas.ScramblingMode.MULTI2; + + /** Advanced Encryption System (AES) 128-bit Encryption mode. */ + public static final int SCRAMBLING_MODE_AES128 = android.hardware.cas.ScramblingMode.AES128; + + /** Advanced Encryption System (AES) Cipher Block Chaining (CBC) mode. */ + public static final int SCRAMBLING_MODE_AES_CBC = android.hardware.cas.ScramblingMode.AES_CBC; + + /** Advanced Encryption System (AES) Electronic Code Book (ECB) mode. */ + public static final int SCRAMBLING_MODE_AES_ECB = android.hardware.cas.ScramblingMode.AES_ECB; + /** * Advanced Encryption System (AES) Society of Cable Telecommunications Engineers (SCTE) 52 * mode. */ public static final int SCRAMBLING_MODE_AES_SCTE52 = - android.hardware.cas.V1_2.ScramblingMode.AES_SCTE52; - /** - * Triple Data Encryption Algorithm (TDES) Electronic Code Book (ECB) mode. - */ - public static final int SCRAMBLING_MODE_TDES_ECB = - android.hardware.cas.V1_2.ScramblingMode.TDES_ECB; + android.hardware.cas.ScramblingMode.AES_SCTE52; + + /** Triple Data Encryption Algorithm (TDES) Electronic Code Book (ECB) mode. */ + public static final int SCRAMBLING_MODE_TDES_ECB = android.hardware.cas.ScramblingMode.TDES_ECB; + /** * Triple Data Encryption Algorithm (TDES) Society of Cable Telecommunications Engineers (SCTE) * 52 mode. */ public static final int SCRAMBLING_MODE_TDES_SCTE52 = - android.hardware.cas.V1_2.ScramblingMode.TDES_SCTE52; + android.hardware.cas.ScramblingMode.TDES_SCTE52; /** * Usages used to open cas sessions. @@ -226,25 +226,21 @@ public final class MediaCas implements AutoCloseable { SESSION_USAGE_TIMESHIFT}) @Retention(RetentionPolicy.SOURCE) public @interface SessionUsage {} - /** - * Cas session is used to descramble live streams. - */ - public static final int SESSION_USAGE_LIVE = android.hardware.cas.V1_2.SessionIntent.LIVE; - /** - * Cas session is used to descramble recoreded streams. - */ - public static final int SESSION_USAGE_PLAYBACK = - android.hardware.cas.V1_2.SessionIntent.PLAYBACK; - /** - * Cas session is used to descramble live streams and encrypt local recorded content - */ - public static final int SESSION_USAGE_RECORD = android.hardware.cas.V1_2.SessionIntent.RECORD; + + /** Cas session is used to descramble live streams. */ + public static final int SESSION_USAGE_LIVE = android.hardware.cas.SessionIntent.LIVE; + + /** Cas session is used to descramble recoreded streams. */ + public static final int SESSION_USAGE_PLAYBACK = android.hardware.cas.SessionIntent.PLAYBACK; + + /** Cas session is used to descramble live streams and encrypt local recorded content */ + public static final int SESSION_USAGE_RECORD = android.hardware.cas.SessionIntent.RECORD; + /** * Cas session is used to descramble live streams , encrypt local recorded content and playback * local encrypted content. */ - public static final int SESSION_USAGE_TIMESHIFT = - android.hardware.cas.V1_2.SessionIntent.TIMESHIFT; + public static final int SESSION_USAGE_TIMESHIFT = android.hardware.cas.SessionIntent.TIMESHIFT; /** * Plugin status events sent from cas system. @@ -261,63 +257,90 @@ public final class MediaCas implements AutoCloseable { * physical CAS modules. */ public static final int PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED = - android.hardware.cas.V1_2.StatusEvent.PLUGIN_PHYSICAL_MODULE_CHANGED; - /** - * The event to indicate that the number of CAS system's session is changed. - */ - public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = - android.hardware.cas.V1_2.StatusEvent.PLUGIN_SESSION_NUMBER_CHANGED; + android.hardware.cas.StatusEvent.PLUGIN_PHYSICAL_MODULE_CHANGED; - private static final Singleton<IMediaCasService> sService = new Singleton<IMediaCasService>() { - @Override - protected IMediaCasService create() { - try { - Log.d(TAG, "Trying to get cas@1.2 service"); - android.hardware.cas.V1_2.IMediaCasService serviceV12 = - android.hardware.cas.V1_2.IMediaCasService.getService(true /*wait*/); - if (serviceV12 != null) { - return serviceV12; + /** The event to indicate that the number of CAS system's session is changed. */ + public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = + android.hardware.cas.StatusEvent.PLUGIN_SESSION_NUMBER_CHANGED; + + private static final Singleton<IMediaCasService> sService = + new Singleton<IMediaCasService>() { + @Override + protected IMediaCasService create() { + try { + Log.d(TAG, "Trying to get AIDL service"); + IMediaCasService serviceAidl = + IMediaCasService.Stub.asInterface( + ServiceManager.getService( + IMediaCasService.DESCRIPTOR + "/default")); + if (serviceAidl != null) { + return serviceAidl; + } + } catch (Exception eAidl) { + Log.d(TAG, "Failed to get cas AIDL service"); + } + return null; } - } catch (Exception eV1_2) { - Log.d(TAG, "Failed to get cas@1.2 service"); - } + }; + + private static final Singleton<android.hardware.cas.V1_0.IMediaCasService> sServiceHidl = + new Singleton<android.hardware.cas.V1_0.IMediaCasService>() { + @Override + protected android.hardware.cas.V1_0.IMediaCasService create() { + try { + Log.d(TAG, "Trying to get cas@1.2 service"); + android.hardware.cas.V1_2.IMediaCasService serviceV12 = + android.hardware.cas.V1_2.IMediaCasService.getService( + true /*wait*/); + if (serviceV12 != null) { + return serviceV12; + } + } catch (Exception eV1_2) { + Log.d(TAG, "Failed to get cas@1.2 service"); + } - try { - Log.d(TAG, "Trying to get cas@1.1 service"); - android.hardware.cas.V1_1.IMediaCasService serviceV11 = - android.hardware.cas.V1_1.IMediaCasService.getService(true /*wait*/); - if (serviceV11 != null) { - return serviceV11; + try { + Log.d(TAG, "Trying to get cas@1.1 service"); + android.hardware.cas.V1_1.IMediaCasService serviceV11 = + android.hardware.cas.V1_1.IMediaCasService.getService( + true /*wait*/); + if (serviceV11 != null) { + return serviceV11; + } + } catch (Exception eV1_1) { + Log.d(TAG, "Failed to get cas@1.1 service"); } - } catch (Exception eV1_1) { - Log.d(TAG, "Failed to get cas@1.1 service"); - } - try { - Log.d(TAG, "Trying to get cas@1.0 service"); - return IMediaCasService.getService(true /*wait*/); - } catch (Exception eV1_0) { - Log.d(TAG, "Failed to get cas@1.0 service"); - } + try { + Log.d(TAG, "Trying to get cas@1.0 service"); + return android.hardware.cas.V1_0.IMediaCasService.getService(true /*wait*/); + } catch (Exception eV1_0) { + Log.d(TAG, "Failed to get cas@1.0 service"); + } - return null; - } - }; + return null; + } + }; static IMediaCasService getService() { return sService.get(); } + static android.hardware.cas.V1_0.IMediaCasService getServiceHidl() { + return sServiceHidl.get(); + } + private void validateInternalStates() { - if (mICas == null) { + if (mICas == null && mICasHidl == null) { throw new IllegalStateException(); } } private void cleanupAndRethrowIllegalState() { mICas = null; - mICasV11 = null; - mICasV12 = null; + mICasHidl = null; + mICasHidl11 = null; + mICasHidl12 = null; throw new IllegalStateException(); } @@ -341,7 +364,7 @@ public final class MediaCas implements AutoCloseable { toBytes((ArrayList<Byte>) msg.obj)); } else if (msg.what == MSG_CAS_SESSION_EVENT) { Bundle bundle = msg.getData(); - ArrayList<Byte> sessionId = toByteArray(bundle.getByteArray(SESSION_KEY)); + byte[] sessionId = bundle.getByteArray(SESSION_KEY); mListener.onSessionEvent(MediaCas.this, createFromSessionId(sessionId), msg.arg1, msg.arg2, bundle.getByteArray(DATA_KEY)); @@ -357,40 +380,94 @@ public final class MediaCas implements AutoCloseable { } } - private final ICasListener.Stub mBinder = new ICasListener.Stub() { - @Override - public void onEvent(int event, int arg, @Nullable ArrayList<Byte> data) - throws RemoteException { - if (mEventHandler != null) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - EventHandler.MSG_CAS_EVENT, event, arg, data)); - } - } - @Override - public void onSessionEvent(@NonNull ArrayList<Byte> sessionId, - int event, int arg, @Nullable ArrayList<Byte> data) - throws RemoteException { - if (mEventHandler != null) { - Message msg = mEventHandler.obtainMessage(); - msg.what = EventHandler.MSG_CAS_SESSION_EVENT; - msg.arg1 = event; - msg.arg2 = arg; - Bundle bundle = new Bundle(); - bundle.putByteArray(EventHandler.SESSION_KEY, toBytes(sessionId)); - bundle.putByteArray(EventHandler.DATA_KEY, toBytes(data)); - msg.setData(bundle); - mEventHandler.sendMessage(msg); - } - } - @Override - public void onStatusUpdate(byte status, int arg) - throws RemoteException { - if (mEventHandler != null) { - mEventHandler.sendMessage(mEventHandler.obtainMessage( - EventHandler.MSG_CAS_STATUS_EVENT, status, arg)); - } - } - }; + private final ICasListener.Stub mBinder = + new ICasListener.Stub() { + @Override + public void onEvent(int event, int arg, byte[] data) throws RemoteException { + if (mEventHandler != null) { + mEventHandler.sendMessage( + mEventHandler.obtainMessage( + EventHandler.MSG_CAS_EVENT, event, arg, data)); + } + } + + @Override + public void onSessionEvent(byte[] sessionId, int event, int arg, byte[] data) + throws RemoteException { + if (mEventHandler != null) { + Message msg = mEventHandler.obtainMessage(); + msg.what = EventHandler.MSG_CAS_SESSION_EVENT; + msg.arg1 = event; + msg.arg2 = arg; + Bundle bundle = new Bundle(); + bundle.putByteArray(EventHandler.SESSION_KEY, sessionId); + bundle.putByteArray(EventHandler.DATA_KEY, data); + msg.setData(bundle); + mEventHandler.sendMessage(msg); + } + } + + @Override + public void onStatusUpdate(byte status, int arg) throws RemoteException { + if (mEventHandler != null) { + mEventHandler.sendMessage( + mEventHandler.obtainMessage( + EventHandler.MSG_CAS_STATUS_EVENT, status, arg)); + } + } + + @Override + public synchronized String getInterfaceHash() throws android.os.RemoteException { + return ICasListener.Stub.HASH; + } + + @Override + public int getInterfaceVersion() throws android.os.RemoteException { + return ICasListener.Stub.VERSION; + } + }; + + private final android.hardware.cas.V1_2.ICasListener.Stub mBinderHidl = + new android.hardware.cas.V1_2.ICasListener.Stub() { + @Override + public void onEvent(int event, int arg, @Nullable ArrayList<Byte> data) + throws RemoteException { + if (mEventHandler != null) { + mEventHandler.sendMessage( + mEventHandler.obtainMessage( + EventHandler.MSG_CAS_EVENT, event, arg, data)); + } + } + + @Override + public void onSessionEvent( + @NonNull ArrayList<Byte> sessionId, + int event, + int arg, + @Nullable ArrayList<Byte> data) + throws RemoteException { + if (mEventHandler != null) { + Message msg = mEventHandler.obtainMessage(); + msg.what = EventHandler.MSG_CAS_SESSION_EVENT; + msg.arg1 = event; + msg.arg2 = arg; + Bundle bundle = new Bundle(); + bundle.putByteArray(EventHandler.SESSION_KEY, toBytes(sessionId)); + bundle.putByteArray(EventHandler.DATA_KEY, toBytes(data)); + msg.setData(bundle); + mEventHandler.sendMessage(msg); + } + } + + @Override + public void onStatusUpdate(byte status, int arg) throws RemoteException { + if (mEventHandler != null) { + mEventHandler.sendMessage( + mEventHandler.obtainMessage( + EventHandler.MSG_CAS_STATUS_EVENT, status, arg)); + } + } + }; private final TunerResourceManager.ResourcesReclaimListener mResourceListener = new TunerResourceManager.ResourcesReclaimListener() { @@ -422,6 +499,11 @@ public final class MediaCas implements AutoCloseable { mName = null; } + PluginDescriptor(@NonNull AidlCasPluginDescriptor descriptor) { + mCASystemId = descriptor.caSystemId; + mName = descriptor.name; + } + PluginDescriptor(@NonNull HidlCasPluginDescriptor descriptor) { mCASystemId = descriptor.caSystemId; mName = descriptor.name; @@ -467,19 +549,20 @@ public final class MediaCas implements AutoCloseable { } return data; } + /** * Class for an open session with the CA system. */ public final class Session implements AutoCloseable { - final ArrayList<Byte> mSessionId; + final byte[] mSessionId; boolean mIsClosed = false; - Session(@NonNull ArrayList<Byte> sessionId) { - mSessionId = new ArrayList<Byte>(sessionId); + Session(@NonNull byte[] sessionId) { + mSessionId = sessionId; } private void validateSessionInternalStates() { - if (mICas == null) { + if (mICas == null && mICasHidl == null) { throw new IllegalStateException(); } if (mIsClosed) { @@ -496,7 +579,7 @@ public final class MediaCas implements AutoCloseable { */ public boolean equals(Object obj) { if (obj instanceof Session) { - return mSessionId.equals(((Session) obj).mSessionId); + return Arrays.equals(mSessionId, ((Session) obj).mSessionId); } return false; } @@ -515,8 +598,13 @@ public final class MediaCas implements AutoCloseable { validateSessionInternalStates(); try { - MediaCasException.throwExceptionIfNeeded( - mICas.setSessionPrivateData(mSessionId, toByteArray(data, 0, data.length))); + if (mICas != null) { + mICas.setSessionPrivateData(mSessionId, data); + } else { + MediaCasException.throwExceptionIfNeeded( + mICasHidl.setSessionPrivateData( + toByteArray(mSessionId), toByteArray(data, 0, data.length))); + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -539,8 +627,13 @@ public final class MediaCas implements AutoCloseable { validateSessionInternalStates(); try { - MediaCasException.throwExceptionIfNeeded( - mICas.processEcm(mSessionId, toByteArray(data, offset, length))); + if (mICas != null) { + mICas.processEcm(mSessionId, data); + } else { + MediaCasException.throwExceptionIfNeeded( + mICasHidl.processEcm( + toByteArray(mSessionId), toByteArray(data, offset, length))); + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -576,15 +669,23 @@ public final class MediaCas implements AutoCloseable { public void sendSessionEvent(int event, int arg, @Nullable byte[] data) throws MediaCasException { validateSessionInternalStates(); + if (mICas != null) { + try { + mICas.sendSessionEvent(mSessionId, event, arg, data); + } catch (RemoteException e) { + cleanupAndRethrowIllegalState(); + } + } - if (mICasV11 == null) { + if (mICasHidl11 == null) { Log.d(TAG, "Send Session Event isn't supported by cas@1.0 interface"); throw new UnsupportedCasException("Send Session Event is not supported"); } try { MediaCasException.throwExceptionIfNeeded( - mICasV11.sendSessionEvent(mSessionId, event, arg, toByteArray(data))); + mICasHidl11.sendSessionEvent( + toByteArray(mSessionId), event, arg, toByteArray(data))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -600,7 +701,7 @@ public final class MediaCas implements AutoCloseable { @NonNull public byte[] getSessionId() { validateSessionInternalStates(); - return toBytes(mSessionId); + return mSessionId; } /** @@ -613,8 +714,12 @@ public final class MediaCas implements AutoCloseable { public void close() { validateSessionInternalStates(); try { - MediaCasStateException.throwExceptionIfNeeded( - mICas.closeSession(mSessionId)); + if (mICas != null) { + mICas.closeSession(mSessionId); + } else { + MediaCasStateException.throwExceptionIfNeeded( + mICasHidl.closeSession(toByteArray(mSessionId))); + } mIsClosed = true; removeSessionFromResourceMap(this); } catch (RemoteException e) { @@ -623,8 +728,8 @@ public final class MediaCas implements AutoCloseable { } } - Session createFromSessionId(@NonNull ArrayList<Byte> sessionId) { - if (sessionId == null || sessionId.size() == 0) { + Session createFromSessionId(byte[] sessionId) { + if (sessionId == null || sessionId.length == 0) { return null; } return new Session(sessionId); @@ -638,12 +743,20 @@ public final class MediaCas implements AutoCloseable { * @return Whether the specified CA system is supported on this device. */ public static boolean isSystemIdSupported(int CA_system_id) { - IMediaCasService service = getService(); - + IMediaCasService service = sService.get(); if (service != null) { try { return service.isSystemIdSupported(CA_system_id); } catch (RemoteException e) { + return false; + } + } + + android.hardware.cas.V1_0.IMediaCasService serviceHidl = sServiceHidl.get(); + if (serviceHidl != null) { + try { + return serviceHidl.isSystemIdSupported(CA_system_id); + } catch (RemoteException e) { } } return false; @@ -655,12 +768,26 @@ public final class MediaCas implements AutoCloseable { * @return an array of descriptors for the available CA plugins. */ public static PluginDescriptor[] enumeratePlugins() { - IMediaCasService service = getService(); - + IMediaCasService service = sService.get(); if (service != null) { try { - ArrayList<HidlCasPluginDescriptor> descriptors = - service.enumeratePlugins(); + AidlCasPluginDescriptor[] descriptors = service.enumeratePlugins(); + if (descriptors.length == 0) { + return null; + } + PluginDescriptor[] results = new PluginDescriptor[descriptors.length]; + for (int i = 0; i < results.length; i++) { + results[i] = new PluginDescriptor(descriptors[i]); + } + return results; + } catch (RemoteException e) { + } + } + + android.hardware.cas.V1_0.IMediaCasService serviceHidl = sServiceHidl.get(); + if (serviceHidl != null) { + try { + ArrayList<HidlCasPluginDescriptor> descriptors = serviceHidl.enumeratePlugins(); if (descriptors.size() == 0) { return null; } @@ -680,29 +807,40 @@ public final class MediaCas implements AutoCloseable { mCasSystemId = casSystemId; mUserId = Process.myUid(); IMediaCasService service = getService(); - android.hardware.cas.V1_2.IMediaCasService serviceV12 = - android.hardware.cas.V1_2.IMediaCasService.castFrom(service); - if (serviceV12 == null) { - android.hardware.cas.V1_1.IMediaCasService serviceV11 = - android.hardware.cas.V1_1.IMediaCasService.castFrom(service); - if (serviceV11 == null) { + if (service != null) { + Log.d(TAG, "Use CAS AIDL interface to create plugin"); + mICas = service.createPlugin(casSystemId, mBinder); + } else { + android.hardware.cas.V1_0.IMediaCasService serviceV10 = getServiceHidl(); + android.hardware.cas.V1_2.IMediaCasService serviceV12 = + android.hardware.cas.V1_2.IMediaCasService.castFrom(serviceV10); + if (serviceV12 == null) { + android.hardware.cas.V1_1.IMediaCasService serviceV11 = + android.hardware.cas.V1_1.IMediaCasService.castFrom(serviceV10); + if (serviceV11 == null) { Log.d(TAG, "Used cas@1_0 interface to create plugin"); - mICas = service.createPlugin(casSystemId, mBinder); - } else { + mICasHidl = serviceV10.createPlugin(casSystemId, mBinderHidl); + } else { Log.d(TAG, "Used cas@1.1 interface to create plugin"); - mICas = mICasV11 = serviceV11.createPluginExt(casSystemId, mBinder); + mICasHidl = + mICasHidl11 = serviceV11.createPluginExt(casSystemId, mBinderHidl); + } + } else { + Log.d(TAG, "Used cas@1.2 interface to create plugin"); + mICasHidl = + mICasHidl11 = + mICasHidl12 = + android.hardware.cas.V1_2.ICas.castFrom( + serviceV12.createPluginExt( + casSystemId, mBinderHidl)); } - } else { - Log.d(TAG, "Used cas@1.2 interface to create plugin"); - mICas = mICasV11 = mICasV12 = - android.hardware.cas.V1_2.ICas - .castFrom(serviceV12.createPluginExt(casSystemId, mBinder)); } } catch(Exception e) { Log.e(TAG, "Failed to create plugin: " + e); mICas = null; + mICasHidl = null; } finally { - if (mICas == null) { + if (mICas == null && mICasHidl == null) { throw new UnsupportedCasException( "Unsupported casSystemId " + casSystemId); } @@ -783,9 +921,22 @@ public final class MediaCas implements AutoCloseable { } IHwBinder getBinder() { + if (mICas != null) { + return null; // Return IHwBinder only for HIDL + } + validateInternalStates(); - return mICas.asBinder(); + return mICasHidl.asBinder(); + } + + /** + * Check if the HAL is an AIDL implementation + * + * @hide + */ + public boolean isAidlHal() { + return mICas != null; } /** @@ -886,8 +1037,12 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - MediaCasException.throwExceptionIfNeeded( - mICas.setPrivateData(toByteArray(data, 0, data.length))); + if (mICas != null) { + mICas.setPrivateData(data); + } else { + MediaCasException.throwExceptionIfNeeded( + mICasHidl.setPrivateData(toByteArray(data, 0, data.length))); + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -899,7 +1054,7 @@ public final class MediaCas implements AutoCloseable { @Override public void onValues(int status, ArrayList<Byte> sessionId) { mStatus = status; - mSession = createFromSessionId(sessionId); + mSession = createFromSessionId(toBytes(sessionId)); } } @@ -912,7 +1067,7 @@ public final class MediaCas implements AutoCloseable { @Override public void onValues(int status, ArrayList<Byte> sessionId) { mStatus = status; - mSession = createFromSessionId(sessionId); + mSession = createFromSessionId(toBytes(sessionId)); } } @@ -971,15 +1126,19 @@ public final class MediaCas implements AutoCloseable { int sessionResourceHandle = getSessionResourceHandle(); try { - OpenSessionCallback cb = new OpenSessionCallback(); - mICas.openSession(cb); - MediaCasException.throwExceptionIfNeeded(cb.mStatus); - addSessionToResourceMap(cb.mSession, sessionResourceHandle); - Log.d(TAG, "Write Stats Log for succeed to Open Session."); - FrameworkStatsLog - .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId, + if (mICasHidl != null) { + OpenSessionCallback cb = new OpenSessionCallback(); + mICasHidl.openSession(cb); + MediaCasException.throwExceptionIfNeeded(cb.mStatus); + addSessionToResourceMap(cb.mSession, sessionResourceHandle); + Log.d(TAG, "Write Stats Log for succeed to Open Session."); + FrameworkStatsLog.write( + FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, + mUserId, + mCasSystemId, FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED); - return cb.mSession; + return cb.mSession; + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -1012,14 +1171,30 @@ public final class MediaCas implements AutoCloseable { throws MediaCasException { int sessionResourceHandle = getSessionResourceHandle(); - if (mICasV12 == null) { + if (mICas != null) { + try { + byte[] sessionId = mICas.openSession(sessionUsage, scramblingMode); + Session session = createFromSessionId(sessionId); + addSessionToResourceMap(session, sessionResourceHandle); + Log.d(TAG, "Write Stats Log for succeed to Open Session."); + FrameworkStatsLog.write( + FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, + mUserId, + mCasSystemId, + FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED); + return session; + } catch (RemoteException e) { + cleanupAndRethrowIllegalState(); + } + } + if (mICasHidl12 == null) { Log.d(TAG, "Open Session with scrambling mode is only supported by cas@1.2+ interface"); throw new UnsupportedCasException("Open Session with scrambling mode is not supported"); } try { OpenSession_1_2_Callback cb = new OpenSession_1_2_Callback(); - mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb); + mICasHidl12.openSession_1_2(sessionUsage, scramblingMode, cb); MediaCasException.throwExceptionIfNeeded(cb.mStatus); addSessionToResourceMap(cb.mSession, sessionResourceHandle); Log.d(TAG, "Write Stats Log for succeed to Open Session."); @@ -1053,8 +1228,12 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - MediaCasException.throwExceptionIfNeeded( - mICas.processEmm(toByteArray(data, offset, length))); + if (mICas != null) { + mICas.processEmm(Arrays.copyOfRange(data, offset, length)); + } else { + MediaCasException.throwExceptionIfNeeded( + mICasHidl.processEmm(toByteArray(data, offset, length))); + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -1092,8 +1271,12 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - MediaCasException.throwExceptionIfNeeded( - mICas.sendEvent(event, arg, toByteArray(data))); + if (mICas != null) { + mICas.sendEvent(event, arg, data); + } else { + MediaCasException.throwExceptionIfNeeded( + mICasHidl.sendEvent(event, arg, toByteArray(data))); + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -1114,8 +1297,11 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - MediaCasException.throwExceptionIfNeeded( - mICas.provision(provisionString)); + if (mICas != null) { + mICas.provision(provisionString); + } else { + MediaCasException.throwExceptionIfNeeded(mICasHidl.provision(provisionString)); + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -1136,8 +1322,12 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - MediaCasException.throwExceptionIfNeeded( - mICas.refreshEntitlements(refreshType, toByteArray(refreshData))); + if (mICas != null) { + mICas.refreshEntitlements(refreshType, refreshData); + } else { + MediaCasException.throwExceptionIfNeeded( + mICasHidl.refreshEntitlements(refreshType, toByteArray(refreshData))); + } } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -1163,6 +1353,13 @@ public final class MediaCas implements AutoCloseable { } finally { mICas = null; } + } else if (mICasHidl != null) { + try { + mICasHidl.release(); + } catch (RemoteException e) { + } finally { + mICasHidl = mICasHidl11 = mICasHidl12 = null; + } } if (mTunerResourceManager != null) { diff --git a/media/java/android/media/MediaDescrambler.java b/media/java/android/media/MediaDescrambler.java index 99bd2549cbc7..b4bdf93db3ab 100644 --- a/media/java/android/media/MediaDescrambler.java +++ b/media/java/android/media/MediaDescrambler.java @@ -17,14 +17,26 @@ package android.media; import android.annotation.NonNull; -import android.hardware.cas.V1_0.*; +import android.hardware.cas.DestinationBuffer; +import android.hardware.cas.IDescrambler; +import android.hardware.cas.ScramblingControl; +import android.hardware.cas.SharedBuffer; +import android.hardware.cas.SubSample; +import android.hardware.cas.V1_0.IDescramblerBase; +import android.hardware.common.Ashmem; +import android.hardware.common.NativeHandle; import android.media.MediaCasException.UnsupportedCasException; import android.os.IHwBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.os.SharedMemory; +import android.system.ErrnoException; import android.util.Log; +import java.io.IOException; import java.nio.ByteBuffer; +import java.util.ArrayList; /** * MediaDescrambler class can be used in conjunction with {@link android.media.MediaCodec} @@ -39,7 +51,198 @@ import java.nio.ByteBuffer; */ public final class MediaDescrambler implements AutoCloseable { private static final String TAG = "MediaDescrambler"; - private IDescramblerBase mIDescrambler; + private DescramblerWrapper mIDescrambler; + + private interface DescramblerWrapper { + + IHwBinder asBinder(); + + int descramble( + @NonNull ByteBuffer srcBuf, + @NonNull ByteBuffer dstBuf, + @NonNull MediaCodec.CryptoInfo cryptoInfo) + throws RemoteException; + + boolean requiresSecureDecoderComponent(@NonNull String mime) throws RemoteException; + + void setMediaCasSession(byte[] sessionId) throws RemoteException; + + void release() throws RemoteException; + } + ; + + private long getSubsampleInfo( + int numSubSamples, + int[] numBytesOfClearData, + int[] numBytesOfEncryptedData, + SubSample[] subSamples) { + long totalSize = 0; + + for (int i = 0; i < numSubSamples; i++) { + totalSize += numBytesOfClearData[i]; + subSamples[i].numBytesOfClearData = numBytesOfClearData[i]; + totalSize += numBytesOfEncryptedData[i]; + subSamples[i].numBytesOfEncryptedData = numBytesOfEncryptedData[i]; + } + return totalSize; + } + + private ParcelFileDescriptor createSharedMemory(ByteBuffer buffer, String name) + throws RemoteException { + byte[] source = buffer.array(); + if (source.length == 0) { + return null; + } + ParcelFileDescriptor fd = null; + try { + SharedMemory ashmem = SharedMemory.create(name == null ? "" : name, source.length); + ByteBuffer ptr = ashmem.mapReadWrite(); + ptr.put(buffer); + ashmem.unmap(ptr); + fd = ashmem.getFdDup(); + return fd; + } catch (ErrnoException | IOException e) { + throw new RemoteException(e); + } + } + + private class AidlDescrambler implements DescramblerWrapper { + + IDescrambler mAidlDescrambler; + + AidlDescrambler(IDescrambler aidlDescrambler) { + mAidlDescrambler = aidlDescrambler; + } + + @Override + public IHwBinder asBinder() { + return null; + } + + @Override + public int descramble( + @NonNull ByteBuffer src, + @NonNull ByteBuffer dst, + @NonNull MediaCodec.CryptoInfo cryptoInfo) + throws RemoteException { + SubSample[] subSamples = new SubSample[cryptoInfo.numSubSamples]; + long totalLength = + getSubsampleInfo( + cryptoInfo.numSubSamples, + cryptoInfo.numBytesOfClearData, + cryptoInfo.numBytesOfEncryptedData, + subSamples); + SharedBuffer srcBuffer = new SharedBuffer(); + DestinationBuffer dstBuffer; + srcBuffer.heapBase = new Ashmem(); + srcBuffer.heapBase.fd = createSharedMemory(src, "Descrambler Source Buffer"); + srcBuffer.heapBase.size = src.array().length; + if (dst == null) { + dstBuffer = DestinationBuffer.nonsecureMemory(srcBuffer); + } else { + ParcelFileDescriptor pfd = + createSharedMemory(dst, "Descrambler Destination Buffer"); + NativeHandle nh = new NativeHandle(); + nh.fds = new ParcelFileDescriptor[] {pfd}; + nh.ints = new int[] {1}; // Mark 1 since source buffer also uses it? + dstBuffer = DestinationBuffer.secureMemory(nh); + } + @ScramblingControl int control = cryptoInfo.key[0]; + + return mAidlDescrambler.descramble( + (byte) control, + subSamples, + srcBuffer, + src.position(), + dstBuffer, + dst.position()); + } + + @Override + public boolean requiresSecureDecoderComponent(@NonNull String mime) throws RemoteException { + return mAidlDescrambler.requiresSecureDecoderComponent(mime); + } + + @Override + public void setMediaCasSession(byte[] sessionId) throws RemoteException { + mAidlDescrambler.setMediaCasSession(sessionId); + } + + @Override + public void release() throws RemoteException { + mAidlDescrambler.release(); + } + } + + private class HidlDescrambler implements DescramblerWrapper { + + IDescramblerBase mHidlDescrambler; + + HidlDescrambler(IDescramblerBase hidlDescrambler) { + mHidlDescrambler = hidlDescrambler; + native_setup(hidlDescrambler.asBinder()); + } + + @Override + public IHwBinder asBinder() { + return mHidlDescrambler.asBinder(); + } + + @Override + public int descramble( + @NonNull ByteBuffer srcBuf, + @NonNull ByteBuffer dstBuf, + @NonNull MediaCodec.CryptoInfo cryptoInfo) + throws RemoteException { + + try { + return native_descramble( + cryptoInfo.key[0], + cryptoInfo.key[1], + cryptoInfo.numSubSamples, + cryptoInfo.numBytesOfClearData, + cryptoInfo.numBytesOfEncryptedData, + srcBuf, + srcBuf.position(), + srcBuf.limit(), + dstBuf, + dstBuf.position(), + dstBuf.limit()); + } catch (ServiceSpecificException e) { + MediaCasStateException.throwExceptionIfNeeded(e.errorCode, e.getMessage()); + } catch (RemoteException e) { + cleanupAndRethrowIllegalState(); + } + return -1; + } + + @Override + public boolean requiresSecureDecoderComponent(@NonNull String mime) throws RemoteException { + return mHidlDescrambler.requiresSecureDecoderComponent(mime); + } + + @Override + public void setMediaCasSession(byte[] sessionId) throws RemoteException { + ArrayList<Byte> byteArray = new ArrayList<>(); + + if (sessionId != null) { + int length = sessionId.length; + byteArray = new ArrayList<Byte>(length); + for (int i = 0; i < length; i++) { + byteArray.add(Byte.valueOf(sessionId[i])); + } + } + + MediaCasStateException.throwExceptionIfNeeded( + mHidlDescrambler.setMediaCasSession(byteArray)); + } + + @Override + public void release() throws RemoteException { + mHidlDescrambler.release(); + native_release(); + } + } private final void validateInternalStates() { if (mIDescrambler == null) { @@ -61,7 +264,14 @@ public final class MediaDescrambler implements AutoCloseable { */ public MediaDescrambler(int CA_system_id) throws UnsupportedCasException { try { - mIDescrambler = MediaCas.getService().createDescrambler(CA_system_id); + if (MediaCas.getService() != null) { + mIDescrambler = + new AidlDescrambler(MediaCas.getService().createDescrambler(CA_system_id)); + } else if (MediaCas.getServiceHidl() != null) { + mIDescrambler = + new HidlDescrambler( + MediaCas.getServiceHidl().createDescrambler(CA_system_id)); + } } catch(Exception e) { Log.e(TAG, "Failed to create descrambler: " + e); mIDescrambler = null; @@ -70,7 +280,6 @@ public final class MediaDescrambler implements AutoCloseable { throw new UnsupportedCasException("Unsupported CA_system_id " + CA_system_id); } } - native_setup(mIDescrambler.asBinder()); } IHwBinder getBinder() { @@ -117,8 +326,7 @@ public final class MediaDescrambler implements AutoCloseable { validateInternalStates(); try { - MediaCasStateException.throwExceptionIfNeeded( - mIDescrambler.setMediaCasSession(session.mSessionId)); + mIDescrambler.setMediaCasSession(session.mSessionId); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -126,27 +334,31 @@ public final class MediaDescrambler implements AutoCloseable { /** * Scramble control value indicating that the samples are not scrambled. + * * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) */ - public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = 0; + public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = (byte) ScramblingControl.UNSCRAMBLED; /** * Scramble control value reserved and shouldn't be used currently. + * * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) */ - public static final byte SCRAMBLE_CONTROL_RESERVED = 1; + public static final byte SCRAMBLE_CONTROL_RESERVED = (byte) ScramblingControl.RESERVED; /** * Scramble control value indicating that the even key is used. + * * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) */ - public static final byte SCRAMBLE_CONTROL_EVEN_KEY = 2; + public static final byte SCRAMBLE_CONTROL_EVEN_KEY = (byte) ScramblingControl.EVENKEY; /** * Scramble control value indicating that the odd key is used. + * * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) */ - public static final byte SCRAMBLE_CONTROL_ODD_KEY = 3; + public static final byte SCRAMBLE_CONTROL_ODD_KEY = (byte) ScramblingControl.ODDKEY; /** * Scramble flag for a hint indicating that the descrambling request is for @@ -207,14 +419,7 @@ public final class MediaDescrambler implements AutoCloseable { } try { - return native_descramble( - cryptoInfo.key[0], - cryptoInfo.key[1], - cryptoInfo.numSubSamples, - cryptoInfo.numBytesOfClearData, - cryptoInfo.numBytesOfEncryptedData, - srcBuf, srcBuf.position(), srcBuf.limit(), - dstBuf, dstBuf.position(), dstBuf.limit()); + return mIDescrambler.descramble(srcBuf, dstBuf, cryptoInfo); } catch (ServiceSpecificException e) { MediaCasStateException.throwExceptionIfNeeded(e.errorCode, e.getMessage()); } catch (RemoteException e) { @@ -233,7 +438,6 @@ public final class MediaDescrambler implements AutoCloseable { mIDescrambler = null; } } - native_release(); } @Override @@ -256,4 +460,4 @@ public final class MediaDescrambler implements AutoCloseable { } private long mNativeContext; -}
\ No newline at end of file +} diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index dab188e40c1f..b11a81047bf8 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -36,7 +36,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -325,14 +324,6 @@ public final class MediaExtractor { } } - private ArrayList<Byte> toByteArray(@NonNull byte[] data) { - ArrayList<Byte> byteArray = new ArrayList<Byte>(data.length); - for (int i = 0; i < data.length; i++) { - byteArray.add(i, Byte.valueOf(data[i])); - } - return byteArray; - } - /** * Retrieves the information about the conditional access system used to scramble * a track. @@ -357,7 +348,7 @@ public final class MediaExtractor { buf.rewind(); final byte[] sessionId = new byte[buf.remaining()]; buf.get(sessionId); - session = mMediaCas.createFromSessionId(toByteArray(sessionId)); + session = mMediaCas.createFromSessionId(sessionId); } return new CasInfo(systemId, session, privateData); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java index 76025ab7aa7d..0446165be5fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java @@ -53,7 +53,9 @@ public class ImageTransformState extends TransformState { return true; } if (otherState instanceof ImageTransformState) { - return mIcon != null && mIcon.sameAs(((ImageTransformState) otherState).getIcon()); + final Icon otherIcon = ((ImageTransformState) otherState).mIcon; + return mIcon == otherIcon || (mIcon != null && otherIcon != null && mIcon.sameAs( + otherIcon)); } return false; } diff --git a/services/core/Android.bp b/services/core/Android.bp index 9268fc03bd2f..bf71e6b8ee6d 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -154,8 +154,8 @@ java_library_static { "android.hardware.health-translate-java", "android.hardware.light-V1-java", "android.hardware.tv.cec-V1.1-java", - "android.hardware.tv.cec-V1-java", - "android.hardware.tv.hdmi-V1-java", + "android.hardware.tv.hdmi.cec-V1-java", + "android.hardware.tv.hdmi.connection-V1-java", "android.hardware.weaver-V1.0-java", "android.hardware.biometrics.face-V1.0-java", "android.hardware.biometrics.fingerprint-V2.3-java", diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index bf0052d31826..dbba9eea15b1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -19,16 +19,16 @@ package com.android.server.hdmi; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.hdmi.HdmiPortInfo; -import android.hardware.tv.cec.CecMessage; -import android.hardware.tv.cec.IHdmiCec; -import android.hardware.tv.cec.IHdmiCecCallback; import android.hardware.tv.cec.V1_0.HotplugEvent; import android.hardware.tv.cec.V1_0.IHdmiCec.getPhysicalAddressCallback; import android.hardware.tv.cec.V1_0.OptionKey; import android.hardware.tv.cec.V1_0.Result; import android.hardware.tv.cec.V1_0.SendMessageResult; -import android.hardware.tv.hdmi.IHdmi; -import android.hardware.tv.hdmi.IHdmiCallback; +import android.hardware.tv.hdmi.cec.CecMessage; +import android.hardware.tv.hdmi.cec.IHdmiCec; +import android.hardware.tv.hdmi.cec.IHdmiCecCallback; +import android.hardware.tv.hdmi.connection.IHdmiConnection; +import android.hardware.tv.hdmi.connection.IHdmiConnectionCallback; import android.icu.util.IllformedLocaleException; import android.icu.util.ULocale; import android.os.Binder; @@ -178,7 +178,7 @@ final class HdmiCecController { if (controller != null) { return controller; } - HdmiLogger.warning("Unable to use CEC and HDMI AIDL HALs"); + HdmiLogger.warning("Unable to use CEC and HDMI Connection AIDL HALs"); controller = createWithNativeWrapper(service, new NativeWrapperImpl11(), atomWriter); if (controller != null) { @@ -872,14 +872,14 @@ final class HdmiCecController { private static final class NativeWrapperImplAidl implements NativeWrapper, IBinder.DeathRecipient { private IHdmiCec mHdmiCec; - private IHdmi mHdmi; + private IHdmiConnection mHdmiConnection; @Nullable private HdmiCecCallback mCallback; private final Object mLock = new Object(); @Override public String nativeInit() { - return connectToHal() ? mHdmiCec.toString() + " " + mHdmi.toString() : null; + return connectToHal() ? mHdmiCec.toString() + " " + mHdmiConnection.toString() : null; } boolean connectToHal() { @@ -896,15 +896,15 @@ final class HdmiCecController { HdmiLogger.error("Couldn't link to death : ", e); } - mHdmi = - IHdmi.Stub.asInterface( - ServiceManager.getService(IHdmi.DESCRIPTOR + "/default")); - if (mHdmi == null) { - HdmiLogger.error("Could not initialize HDMI AIDL HAL"); + mHdmiConnection = + IHdmiConnection.Stub.asInterface( + ServiceManager.getService(IHdmiConnection.DESCRIPTOR + "/default")); + if (mHdmiConnection == null) { + HdmiLogger.error("Could not initialize HDMI Connection AIDL HAL"); return false; } try { - mHdmi.asBinder().linkToDeath(this, 0); + mHdmiConnection.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { HdmiLogger.error("Couldn't link to death : ", e); } @@ -915,8 +915,8 @@ final class HdmiCecController { public void binderDied() { // One of the services died, try to reconnect to both. mHdmiCec.asBinder().unlinkToDeath(this, 0); - mHdmi.asBinder().unlinkToDeath(this, 0); - HdmiLogger.error("HDMI or CEC service died, reconnecting"); + mHdmiConnection.asBinder().unlinkToDeath(this, 0); + HdmiLogger.error("HDMI Connection or CEC service died, reconnecting"); connectToHal(); // Reconnect the callback if (mCallback != null) { @@ -935,7 +935,7 @@ final class HdmiCecController { } try { // Create an AIDL callback that can callback onHotplugEvent - mHdmi.setCallback(new HdmiCallbackAidl(callback)); + mHdmiConnection.setCallback(new HdmiConnectionCallbackAidl(callback)); } catch (RemoteException e) { HdmiLogger.error("Couldn't initialise tv.hdmi callback : ", e); } @@ -1052,10 +1052,11 @@ final class HdmiCecController { @Override public HdmiPortInfo[] nativeGetPortInfos() { try { - android.hardware.tv.hdmi.HdmiPortInfo[] hdmiPortInfos = mHdmi.getPortInfo(); + android.hardware.tv.hdmi.connection.HdmiPortInfo[] hdmiPortInfos = + mHdmiConnection.getPortInfo(); HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.length]; int i = 0; - for (android.hardware.tv.hdmi.HdmiPortInfo portInfo : hdmiPortInfos) { + for (android.hardware.tv.hdmi.connection.HdmiPortInfo portInfo : hdmiPortInfos) { hdmiPortInfo[i] = new HdmiPortInfo( portInfo.portId, @@ -1076,7 +1077,7 @@ final class HdmiCecController { @Override public boolean nativeIsConnected(int port) { try { - return mHdmi.isConnected(port); + return mHdmiConnection.isConnected(port); } catch (RemoteException e) { HdmiLogger.error("Failed to get connection info : ", e); return false; @@ -1580,10 +1581,10 @@ final class HdmiCecController { } } - private static final class HdmiCallbackAidl extends IHdmiCallback.Stub { + private static final class HdmiConnectionCallbackAidl extends IHdmiConnectionCallback.Stub { private final HdmiCecCallback mHdmiCecCallback; - HdmiCallbackAidl(HdmiCecCallback hdmiCecCallback) { + HdmiConnectionCallbackAidl(HdmiCecCallback hdmiCecCallback) { mHdmiCecCallback = hdmiCecCallback; } @@ -1594,12 +1595,12 @@ final class HdmiCecController { @Override public synchronized String getInterfaceHash() throws android.os.RemoteException { - return IHdmiCallback.Stub.HASH; + return IHdmiConnectionCallback.Stub.HASH; } @Override public int getInterfaceVersion() throws android.os.RemoteException { - return IHdmiCallback.Stub.VERSION; + return IHdmiConnectionCallback.Stub.VERSION; } } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 4d525da220c7..9b42cfca2e68 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -36,6 +36,7 @@ import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.NetworkRequest; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; @@ -103,12 +104,12 @@ class RebootEscrowManager { /** * Number of boots until we consider the escrow data to be stale for the purposes of metrics. - * <p> - * If the delta between the current boot number and the boot number stored when the mechanism + * + * <p>If the delta between the current boot number and the boot number stored when the mechanism * was armed is under this number and the escrow mechanism fails, we report it as a failure of * the mechanism. - * <p> - * If the delta over this number and escrow fails, we will not report the metric as failed + * + * <p>If the delta over this number and escrow fails, we will not report the metric as failed * since there most likely was some other issue if the device rebooted several times before * getting to the escrow restore code. */ @@ -120,8 +121,11 @@ class RebootEscrowManager { */ private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3; private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30; + // 3 minutes. It's enough for the default 3 retries with 30 seconds interval - private static final int DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS = 180_000; + private static final int DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS = 180_000; + // 5 seconds. An extension of the overall RoR timeout to account for overhead. + private static final int DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS = 5000; @IntDef(prefix = {"ERROR_"}, value = { ERROR_NONE, @@ -133,6 +137,7 @@ class RebootEscrowManager { ERROR_PROVIDER_MISMATCH, ERROR_KEYSTORE_FAILURE, ERROR_NO_NETWORK, + ERROR_TIMEOUT_EXHAUSTED, }) @Retention(RetentionPolicy.SOURCE) @interface RebootEscrowErrorCode { @@ -147,6 +152,7 @@ class RebootEscrowManager { static final int ERROR_PROVIDER_MISMATCH = 6; static final int ERROR_KEYSTORE_FAILURE = 7; static final int ERROR_NO_NETWORK = 8; + static final int ERROR_TIMEOUT_EXHAUSTED = 9; private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; @@ -168,6 +174,15 @@ class RebootEscrowManager { /** Notified when mRebootEscrowReady changes. */ private RebootEscrowListener mRebootEscrowListener; + /** Set when unlocking reboot escrow times out. */ + private boolean mRebootEscrowTimedOut = false; + + /** + * Set when {@link #loadRebootEscrowDataWithRetry} is called to ensure the function is only + * called once. + */ + private boolean mLoadEscrowDataWithRetry = false; + /** * Hold this lock when checking or generating the reboot escrow key. */ @@ -192,6 +207,7 @@ class RebootEscrowManager { PowerManager.WakeLock mWakeLock; + private ConnectivityManager.NetworkCallback mNetworkCallback; interface Callbacks { boolean isUserSecure(int userId); @@ -246,6 +262,11 @@ class RebootEscrowManager { "server_based_ror_enabled", false); } + public boolean waitForInternet() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_OTA, "wait_for_internet_ror", false); + } + public boolean isNetworkConnected() { final ConnectivityManager connectivityManager = mContext.getSystemService(ConnectivityManager.class); @@ -263,6 +284,38 @@ class RebootEscrowManager { NetworkCapabilities.NET_CAPABILITY_VALIDATED); } + /** + * Request network with internet connectivity with timeout. + * + * @param networkCallback callback to be executed if connectivity manager exists. + * @return true if success + */ + public boolean requestNetworkWithInternet( + ConnectivityManager.NetworkCallback networkCallback) { + final ConnectivityManager connectivityManager = + mContext.getSystemService(ConnectivityManager.class); + if (connectivityManager == null) { + return false; + } + NetworkRequest request = + new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + + connectivityManager.requestNetwork( + request, networkCallback, getLoadEscrowTimeoutMillis()); + return true; + } + + public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) { + final ConnectivityManager connectivityManager = + mContext.getSystemService(ConnectivityManager.class); + if (connectivityManager == null) { + return; + } + connectivityManager.unregisterNetworkCallback(networkCallback); + } + public Context getContext() { return mContext; } @@ -318,6 +371,16 @@ class RebootEscrowManager { DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS); } + @VisibleForTesting + public int getLoadEscrowTimeoutMillis() { + return DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS; + } + + @VisibleForTesting + public int getWakeLockTimeoutMillis() { + return getLoadEscrowTimeoutMillis() + DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS; + } + public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootCompleteInSeconds) { @@ -351,13 +414,37 @@ class RebootEscrowManager { mKeyStoreManager = injector.getKeyStoreManager(); } - private void onGetRebootEscrowKeyFailed(List<UserInfo> users, int attemptCount) { + /** Wrapper function to set error code serialized through handler, */ + private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) { + if (mInjector.waitForInternet()) { + mInjector.post( + handler, + () -> { + mLoadEscrowDataErrorCode = value; + }); + } else { + mLoadEscrowDataErrorCode = value; + } + } + + /** Wrapper function to compare and set error code serialized through handler. */ + private void compareAndSetLoadEscrowDataErrorCode( + @RebootEscrowErrorCode int expectedValue, + @RebootEscrowErrorCode int newValue, + Handler handler) { + if (expectedValue == mLoadEscrowDataErrorCode) { + setLoadEscrowDataErrorCode(newValue, handler); + } + } + + private void onGetRebootEscrowKeyFailed( + List<UserInfo> users, int attemptCount, Handler retryHandler) { Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage."); for (UserInfo user : users) { mStorage.removeRebootEscrow(user.id); } - onEscrowRestoreComplete(false, attemptCount); + onEscrowRestoreComplete(false, attemptCount, retryHandler); } void loadRebootEscrowDataIfAvailable(Handler retryHandler) { @@ -380,39 +467,130 @@ class RebootEscrowManager { mWakeLock = mInjector.getWakeLock(); if (mWakeLock != null) { mWakeLock.setReferenceCounted(false); - mWakeLock.acquire(DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS); + mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis()); + } + + if (mInjector.waitForInternet()) { + // Timeout to stop retrying same as the wake lock timeout. + mInjector.postDelayed( + retryHandler, + () -> { + mRebootEscrowTimedOut = true; + }, + mInjector.getLoadEscrowTimeoutMillis()); + + mInjector.post( + retryHandler, + () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers)); + return; } mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry( retryHandler, 0, users, rebootEscrowUsers)); } - void scheduleLoadRebootEscrowDataOrFail(Handler retryHandler, int attemptNumber, - List<UserInfo> users, List<UserInfo> rebootEscrowUsers) { + void scheduleLoadRebootEscrowDataOrFail( + Handler retryHandler, + int attemptNumber, + List<UserInfo> users, + List<UserInfo> rebootEscrowUsers) { Objects.requireNonNull(retryHandler); final int retryLimit = mInjector.getLoadEscrowDataRetryLimit(); final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds(); - if (attemptNumber < retryLimit) { + if (attemptNumber < retryLimit && !mRebootEscrowTimedOut) { Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber); mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry( - retryHandler, attemptNumber, users, rebootEscrowUsers), + retryHandler, attemptNumber, users, rebootEscrowUsers), retryIntervalInSeconds * 1000); return; } + if (mInjector.waitForInternet()) { + if (mRebootEscrowTimedOut) { + Slog.w(TAG, "Failed to load reboot escrow data within timeout"); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler); + } else { + Slog.w( + TAG, + "Failed to load reboot escrow data after " + attemptNumber + " attempts"); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler); + } + onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); + return; + } + Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) { mLoadEscrowDataErrorCode = ERROR_NO_NETWORK; } else { mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; } - onGetRebootEscrowKeyFailed(users, attemptNumber); + onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); } - void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber, - List<UserInfo> users, List<UserInfo> rebootEscrowUsers) { + void loadRebootEscrowDataOnInternet( + Handler retryHandler, List<UserInfo> users, List<UserInfo> rebootEscrowUsers) { + + // HAL-Based RoR does not require network connectivity. + if (!mInjector.serverBasedResumeOnReboot()) { + loadRebootEscrowDataWithRetry( + retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers); + return; + } + + mNetworkCallback = + new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + compareAndSetLoadEscrowDataErrorCode( + ERROR_NO_NETWORK, ERROR_NONE, retryHandler); + + if (!mLoadEscrowDataWithRetry) { + mLoadEscrowDataWithRetry = true; + // Only kickoff retry mechanism on first onAvailable call. + loadRebootEscrowDataWithRetry( + retryHandler, + /* attemptNumber = */ 0, + users, + rebootEscrowUsers); + } + } + + @Override + public void onUnavailable() { + Slog.w(TAG, "Failed to connect to network within timeout"); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_NO_NETWORK, retryHandler); + onGetRebootEscrowKeyFailed(users, /* attemptCount= */ 0, retryHandler); + } + + @Override + public void onLost(Network lostNetwork) { + // TODO(b/231660348): If network is lost, wait for network to become + // available again. + Slog.w(TAG, "Network lost, still attempting to load escrow key."); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_NO_NETWORK, retryHandler); + } + }; + + // Fallback to retrying without waiting for internet on failure. + boolean success = mInjector.requestNetworkWithInternet(mNetworkCallback); + if (!success) { + loadRebootEscrowDataWithRetry( + retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers); + } + } + + void loadRebootEscrowDataWithRetry( + Handler retryHandler, + int attemptNumber, + List<UserInfo> users, + List<UserInfo> rebootEscrowUsers) { // Fetch the key from keystore to decrypt the escrow data & escrow key; this key is // generated before reboot. Note that we will clear the escrow key even if the keystore key // is null. @@ -423,7 +601,7 @@ class RebootEscrowManager { RebootEscrowKey escrowKey; try { - escrowKey = getAndClearRebootEscrowKey(kk); + escrowKey = getAndClearRebootEscrowKey(kk, retryHandler); } catch (IOException e) { Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e); scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users, @@ -438,12 +616,12 @@ class RebootEscrowManager { ? RebootEscrowProviderInterface.TYPE_SERVER_BASED : RebootEscrowProviderInterface.TYPE_HAL; if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) { - mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH; + setLoadEscrowDataErrorCode(ERROR_PROVIDER_MISMATCH, retryHandler); } else { - mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY; + setLoadEscrowDataErrorCode(ERROR_LOAD_ESCROW_KEY, retryHandler); } } - onGetRebootEscrowKeyFailed(users, attemptNumber + 1); + onGetRebootEscrowKeyFailed(users, attemptNumber + 1, retryHandler); return; } @@ -454,10 +632,10 @@ class RebootEscrowManager { allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk); } - if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) { - mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS; + if (!allUsersUnlocked) { + compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNLOCK_ALL_USERS, retryHandler); } - onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1); + onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1, retryHandler); } private void clearMetricsStorage() { @@ -497,7 +675,8 @@ class RebootEscrowManager { .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; } - private void reportMetricOnRestoreComplete(boolean success, int attemptCount) { + private void reportMetricOnRestoreComplete( + boolean success, int attemptCount, Handler retryHandler) { int serviceType = mInjector.serverBasedResumeOnReboot() ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED : FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL; @@ -511,52 +690,69 @@ class RebootEscrowManager { } int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete(); - if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) { - mLoadEscrowDataErrorCode = ERROR_UNKNOWN; - } - - Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: " - + serviceType + ", error code: " + mLoadEscrowDataErrorCode); + if (!success) { + compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNKNOWN, retryHandler); + } + + Slog.i( + TAG, + "Reporting RoR recovery metrics, success: " + + success + + ", service type: " + + serviceType + + ", error code: " + + mLoadEscrowDataErrorCode); // TODO(179105110) report the duration since boot complete. - mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount, - escrowDurationInSeconds, vbmetaDigestStatus, -1); - - mLoadEscrowDataErrorCode = ERROR_NONE; + mInjector.reportMetric( + success, + mLoadEscrowDataErrorCode, + serviceType, + attemptCount, + escrowDurationInSeconds, + vbmetaDigestStatus, + -1); + + setLoadEscrowDataErrorCode(ERROR_NONE, retryHandler); } - private void onEscrowRestoreComplete(boolean success, int attemptCount) { + private void onEscrowRestoreComplete(boolean success, int attemptCount, Handler retryHandler) { int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM); int bootCountDelta = mInjector.getBootCount() - previousBootCount; if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) { - reportMetricOnRestoreComplete(success, attemptCount); + reportMetricOnRestoreComplete(success, attemptCount, retryHandler); } - // Clear the old key in keystore. A new key will be generated by new RoR requests. mKeyStoreManager.clearKeyStoreEncryptionKey(); // Clear the saved reboot escrow provider mInjector.clearRebootEscrowProvider(); clearMetricsStorage(); + if (mNetworkCallback != null) { + mInjector.stopRequestingNetwork(mNetworkCallback); + } + if (mWakeLock != null) { mWakeLock.release(); } } - private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException { + private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk, Handler retryHandler) + throws IOException { RebootEscrowProviderInterface rebootEscrowProvider = mInjector.createRebootEscrowProviderIfNeeded(); if (rebootEscrowProvider == null) { - Slog.w(TAG, + Slog.w( + TAG, "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); - mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER; + setLoadEscrowDataErrorCode(ERROR_NO_PROVIDER, retryHandler); return null; } // Server based RoR always need the decryption key from keystore. if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED && kk == null) { - mLoadEscrowDataErrorCode = ERROR_KEYSTORE_FAILURE; + setLoadEscrowDataErrorCode(ERROR_KEYSTORE_FAILURE, retryHandler); return null; } @@ -870,6 +1066,9 @@ class RebootEscrowManager { pw.print("mRebootEscrowListener="); pw.println(mRebootEscrowListener); + pw.print("mLoadEscrowDataErrorCode="); + pw.println(mLoadEscrowDataErrorCode); + boolean keySet; synchronized (mKeyGenerationLock) { keySet = mPendingRebootEscrowKey != null; diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java index 14075301d523..e0561519c23b 100644 --- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java +++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java @@ -298,7 +298,9 @@ public class ArtStatsLogUtils { dexMetadataType, apkType, ISA_MAP.getOrDefault(isa, - ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN)); + ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN), + ArtStatsLog.ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_UNKNOWN, + ArtStatsLog.ART_DATUM_REPORTED__UFFD_SUPPORT__ART_UFFD_SUPPORT_UNKNOWN); } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index ed4ba0d0eacd..7f8f40626c56 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -3202,8 +3202,7 @@ public final class PowerManagerService extends SystemService } final PowerGroup powerGroup = mPowerGroups.get(groupId); wakefulness = powerGroup.getWakefulnessLocked(); - if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) && - powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) { + if (powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) { startDreaming = canDreamLocked(powerGroup) || canDozeLocked(powerGroup); powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false); } else { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index b01c1c8ead28..ce6bd6ccbe51 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -50,6 +50,8 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.pm.UserInfo; import android.hardware.rebootescrow.IRebootEscrow; +import android.net.ConnectivityManager; +import android.net.Network; import android.os.Handler; import android.os.HandlerThread; import android.os.RemoteException; @@ -72,6 +74,7 @@ import org.mockito.ArgumentCaptor; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.function.Consumer; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -113,6 +116,7 @@ public class RebootEscrowManagerTests { private RebootEscrowManager mService; private SecretKey mAesKey; private MockInjector mMockInjector; + private Handler mHandler; public interface MockableRebootEscrowInjected { int getBootCount(); @@ -132,6 +136,9 @@ public class RebootEscrowManagerTests { private final RebootEscrowKeyStoreManager mKeyStoreManager; private boolean mServerBased; private RebootEscrowProviderInterface mRebootEscrowProviderInUse; + private ConnectivityManager.NetworkCallback mNetworkCallback; + private Consumer<ConnectivityManager.NetworkCallback> mNetworkConsumer; + private boolean mWaitForInternet; MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow, @@ -142,6 +149,7 @@ public class RebootEscrowManagerTests { mRebootEscrow = rebootEscrow; mServiceConnection = null; mServerBased = false; + mWaitForInternet = false; RebootEscrowProviderHalImpl.Injector halInjector = new RebootEscrowProviderHalImpl.Injector() { @Override @@ -164,6 +172,7 @@ public class RebootEscrowManagerTests { mServiceConnection = serviceConnection; mRebootEscrow = null; mServerBased = true; + mWaitForInternet = false; RebootEscrowProviderServerBasedImpl.Injector injector = new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) { @Override @@ -199,11 +208,33 @@ public class RebootEscrowManagerTests { } @Override + public boolean waitForInternet() { + return mWaitForInternet; + } + + public void setWaitForNetwork(boolean waitForNetworkEnabled) { + mWaitForInternet = waitForNetworkEnabled; + } + + @Override public boolean isNetworkConnected() { return false; } @Override + public boolean requestNetworkWithInternet( + ConnectivityManager.NetworkCallback networkCallback) { + mNetworkCallback = networkCallback; + mNetworkConsumer.accept(networkCallback); + return true; + } + + @Override + public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) { + mNetworkCallback = null; + } + + @Override public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() { mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider; return mRebootEscrowProviderInUse; @@ -242,6 +273,12 @@ public class RebootEscrowManagerTests { } @Override + public int getLoadEscrowTimeoutMillis() { + // Timeout in 3 seconds. + return 3000; + } + + @Override public String getVbmetaDigest(boolean other) { return other ? "" : "fake digest"; } @@ -291,6 +328,9 @@ public class RebootEscrowManagerTests { mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow, mKeyStoreManager, mStorage, mInjected); mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage); + HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); + thread.start(); + mHandler = new Handler(thread.getLooper()); } private void setServerBasedRebootEscrowProvider() throws Exception { @@ -462,7 +502,7 @@ public class RebootEscrowManagerTests { @Test public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception { - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); } @Test @@ -499,7 +539,7 @@ public class RebootEscrowManagerTests { eq(20), eq(0) /* vbmeta status */, anyInt()); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue()); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mRebootEscrow).retrieveKey(); assertTrue(metricsSuccessCaptor.getValue()); verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); @@ -531,9 +571,16 @@ public class RebootEscrowManagerTests { // pretend reboot happens here when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */, - anyInt(), eq(0) /* vbmeta status */, anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + eq(0) /* error code */, + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); @@ -569,15 +616,23 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, - eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class); mService.loadRebootEscrowDataIfAvailable(null); verify(mServiceConnection).unwrap(any(), anyLong()); assertFalse(metricsSuccessCaptor.getValue()); - assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), metricsErrorCodeCaptor.getValue()); } @@ -606,18 +661,24 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, - eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(2) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); - HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); - thread.start(); - mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper())); + mService.loadRebootEscrowDataIfAvailable(mHandler); // Sleep 5s for the retry to complete Thread.sleep(5 * 1000); assertFalse(metricsSuccessCaptor.getValue()); - assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), metricsErrorCodeCaptor.getValue()); } @@ -645,16 +706,22 @@ public class RebootEscrowManagerTests { // pretend reboot happens here when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - anyInt(), anyInt(), eq(2) /* attempt count */, anyInt(), anyInt(), anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + anyInt(), + anyInt(), + eq(2) /* attempt count */, + anyInt(), + anyInt(), + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())) .thenThrow(new IOException()) .thenAnswer(invocation -> invocation.getArgument(0)); - HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); - thread.start(); - mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper())); + mService.loadRebootEscrowDataIfAvailable(mHandler); // Sleep 5s for the retry to complete Thread.sleep(5 * 1000); verify(mServiceConnection, times(2)).unwrap(any(), anyLong()); @@ -663,6 +730,447 @@ public class RebootEscrowManagerTests { } @Test + public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternet_success() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + eq(0) /* error code */, + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load escrow data + when(mServiceConnection.unwrap(any(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternetRemoteException_Failure() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load escrow data + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_networkUnavailable() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(0) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // Network is not available within timeout. + mMockInjector.mNetworkConsumer = ConnectivityManager.NetworkCallback::onUnavailable; + mService.loadRebootEscrowDataIfAvailable(mHandler); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_networkLost() throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(2) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // Network is available, then lost. + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException()); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + callback.onLost(mockNetwork); + }; + mService.loadRebootEscrowDataIfAvailable(mHandler); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_networkAvailableWithDelay() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load escrow data + when(mServiceConnection.unwrap(any(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + // network available after 1 sec + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + callback.onAvailable(mockNetwork); + }; + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_timeoutExhausted() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load reboot escrow data + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); + Network mockNetwork = mock(Network.class); + // wait past timeout + mMockInjector.mNetworkConsumer = + (callback) -> { + try { + Thread.sleep(3500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + callback.onAvailable(mockNetwork); + }; + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_TIMEOUT_EXHAUSTED), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_serverBasedWaitForNetwork_retryCountExhausted() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(2) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException()); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + verify(mServiceConnection, times(2)).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedWaitForInternet_RetrySuccess() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + anyInt(), + anyInt(), + eq(2) /* attempt count */, + anyInt(), + anyInt(), + anyInt()); + + when(mServiceConnection.unwrap(any(), anyLong())) + .thenThrow(new IOException()) + .thenAnswer(invocation -> invocation.getArgument(0)); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + verify(mServiceConnection, times(2)).unwrap(any(), anyLong()); + assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception { when(mInjected.getBootCount()).thenReturn(0); @@ -687,7 +1195,7 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(10); when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mRebootEscrow).retrieveKey(); verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()); @@ -715,7 +1223,7 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(10); when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()); } @@ -753,7 +1261,7 @@ public class RebootEscrowManagerTests { // Trigger a vbmeta digest mismatch mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, "non sense value", USER_SYSTEM); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt()); assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, @@ -790,7 +1298,7 @@ public class RebootEscrowManagerTests { eq(1) /* attempt count */, anyInt(), anyInt(), anyInt()); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mRebootEscrow).retrieveKey(); assertFalse(metricsSuccessCaptor.getValue()); assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index f373f6ece5ad..ac148e7d05d7 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1048,6 +1048,13 @@ public class SubscriptionManager { public static final String ALLOWED_NETWORK_TYPES = SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS; + /** + * TelephonyProvider column name for user handle associated with a sim. + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String USER_HANDLE = SimInfo.COLUMN_USER_HANDLE; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"USAGE_SETTING_"}, diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index f65b7c2a4ca7..945b0850d25e 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -1828,6 +1828,8 @@ public class ProvisioningManager { return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId); } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); } } |