summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp27
-rw-r--r--Android.bp1
-rw-r--r--Ravenwood.bp6
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java2
-rw-r--r--apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java7
-rw-r--r--apex/jobscheduler/service/Android.bp1
-rw-r--r--apex/jobscheduler/service/aconfig/alarm.aconfig7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java23
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java141
-rw-r--r--api/Android.bp35
-rw-r--r--api/StubLibraries.bp26
-rw-r--r--cmds/idmap2/tests/FabricatedOverlayTests.cpp2
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp8
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp2
-rw-r--r--cmds/idmap2/tests/TestHelpers.h21
-rw-r--r--cmds/svc/src/com/android/commands/svc/PowerCommand.java12
-rw-r--r--core/api/current.txt35
-rw-r--r--core/api/module-lib-current.txt1
-rw-r--r--core/api/system-current.txt32
-rw-r--r--core/api/test-current.txt39
-rw-r--r--core/api/test-lint-baseline.txt10
-rw-r--r--core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java31
-rw-r--r--core/java/android/app/Activity.java6
-rw-r--r--core/java/android/app/ActivityThread.java46
-rw-r--r--core/java/android/app/AutomaticZenRule.java18
-rw-r--r--core/java/android/app/ClientTransactionHandler.java7
-rw-r--r--core/java/android/app/GrammaticalInflectionManager.java3
-rw-r--r--core/java/android/app/LocalActivityManager.java4
-rw-r--r--core/java/android/app/LocaleConfig.java3
-rw-r--r--core/java/android/app/NotificationManager.java72
-rw-r--r--core/java/android/app/ResourcesManager.java137
-rw-r--r--core/java/android/app/admin/AccountTypePolicyKey.java4
-rw-r--r--core/java/android/app/admin/BundlePolicyValue.java7
-rw-r--r--core/java/android/app/admin/ComponentNamePolicyValue.java5
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java2
-rw-r--r--core/java/android/app/admin/IntentFilterPolicyKey.java4
-rw-r--r--core/java/android/app/admin/LockTaskPolicy.java5
-rw-r--r--core/java/android/app/admin/PackagePermissionPolicyKey.java4
-rw-r--r--core/java/android/app/admin/PackagePolicyKey.java4
-rw-r--r--core/java/android/app/admin/PolicySizeVerifier.java63
-rw-r--r--core/java/android/app/admin/StringPolicyValue.java5
-rw-r--r--core/java/android/app/admin/StringSetPolicyValue.java5
-rw-r--r--core/java/android/app/admin/UserRestrictionPolicyKey.java4
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig16
-rw-r--r--core/java/android/app/backup/BackupManagerMonitor.java28
-rw-r--r--core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java17
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java43
-rw-r--r--core/java/android/app/servertransaction/ClientTransactionListenerController.java65
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java16
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java15
-rw-r--r--core/java/android/app/servertransaction/StopActivityItem.java41
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java6
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutorHelper.java2
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceInternal.java21
-rw-r--r--core/java/android/content/Intent.java6
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl1
-rw-r--r--core/java/android/content/pm/LauncherApps.java22
-rw-r--r--core/java/android/content/pm/PackageManager.java9
-rw-r--r--core/java/android/content/pm/PermissionInfo.java4
-rw-r--r--core/java/android/content/pm/UserInfo.java7
-rw-r--r--core/java/android/content/pm/flags.aconfig8
-rw-r--r--core/java/android/content/res/AssetManager.java10
-rw-r--r--core/java/android/content/res/Resources.java8
-rw-r--r--core/java/android/content/res/ResourcesImpl.java10
-rw-r--r--core/java/android/credentials/CredentialManager.java3
-rw-r--r--core/java/android/credentials/selection/IntentFactory.java106
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java8
-rw-r--r--core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl24
-rw-r--r--core/java/android/hardware/ISensorPrivacyManager.aidl7
-rw-r--r--core/java/android/hardware/SensorPrivacyManager.java84
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java14
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java23
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java4
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java2
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java1
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl6
-rw-r--r--core/java/android/hardware/input/InputManager.java8
-rw-r--r--core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl (renamed from packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/GoneSceneModule.kt)8
-rw-r--r--core/java/android/hardware/input/KeyboardLayoutSelectionResult.java260
-rw-r--r--core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig7
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java4
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java162
-rw-r--r--core/java/android/os/BatteryConsumer.java2
-rw-r--r--core/java/android/os/BatteryStats.java6
-rw-r--r--core/java/android/os/GraphicsEnvironment.java11
-rw-r--r--core/java/android/os/HwParcel.java2
-rw-r--r--core/java/android/os/IVibratorManagerService.aidl2
-rw-r--r--core/java/android/os/MessageQueue.java10
-rw-r--r--core/java/android/os/SystemVibrator.java5
-rw-r--r--core/java/android/os/SystemVibratorManager.java10
-rw-r--r--core/java/android/os/UserManager.java5
-rw-r--r--core/java/android/os/VibrationAttributes.java11
-rw-r--r--core/java/android/os/Vibrator.java4
-rw-r--r--core/java/android/os/VibratorManager.java4
-rw-r--r--core/java/android/os/flags.aconfig2
-rw-r--r--core/java/android/os/health/HealthStatsWriter.java2
-rw-r--r--core/java/android/os/image/DynamicSystemClient.java2
-rw-r--r--core/java/android/os/image/DynamicSystemManager.java2
-rw-r--r--core/java/android/os/storage/StorageManager.java2
-rw-r--r--core/java/android/os/storage/StorageVolume.java2
-rw-r--r--core/java/android/os/vibrator/VibrationConfig.java14
-rw-r--r--core/java/android/permission/PermissionManager.java50
-rw-r--r--core/java/android/permission/flags.aconfig8
-rw-r--r--core/java/android/provider/CallLog.java3
-rw-r--r--core/java/android/provider/E2eeContactKeysManager.java402
-rw-r--r--core/java/android/provider/Settings.java24
-rw-r--r--core/java/android/security/ConfirmationPrompt.java1
-rw-r--r--core/java/android/security/keystore/recovery/RecoveryController.java7
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java20
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java17
-rw-r--r--core/java/android/service/notification/ZenAdapters.java (renamed from services/core/java/com/android/server/notification/ZenAdapters.java)74
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java69
-rw-r--r--core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl2
-rw-r--r--core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl6
-rw-r--r--core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl14
-rw-r--r--core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl (renamed from packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt)26
-rw-r--r--core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java118
-rw-r--r--core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java61
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java21
-rw-r--r--core/java/android/tracing/flags.aconfig2
-rw-r--r--core/java/android/view/HandwritingInitiator.java33
-rw-r--r--core/java/android/view/IWindow.aidl4
-rw-r--r--core/java/android/view/IWindowSession.aidl4
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java34
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java16
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java1
-rw-r--r--core/java/android/view/InsetsController.java72
-rw-r--r--core/java/android/view/InsetsResizeAnimationRunner.java2
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java2
-rw-r--r--core/java/android/view/MotionEvent.java89
-rw-r--r--core/java/android/view/PointerIcon.java12
-rw-r--r--core/java/android/view/ScrollFeedbackProvider.java7
-rw-r--r--core/java/android/view/View.java32
-rw-r--r--core/java/android/view/ViewRootImpl.java17
-rw-r--r--core/java/android/view/WindowInsets.java37
-rw-r--r--core/java/android/view/WindowlessWindowManager.java6
-rw-r--r--core/java/android/view/autofill/AutofillManager.java2
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java48
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java184
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java12
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java123
-rw-r--r--core/java/android/webkit/WebViewDelegate.java8
-rw-r--r--core/java/android/webkit/WebViewFactory.java14
-rw-r--r--core/java/android/webkit/WebViewProviderResponse.java2
-rw-r--r--core/java/android/webkit/WebViewUpdateManager.java4
-rw-r--r--core/java/android/widget/HorizontalScrollView.java12
-rw-r--r--core/java/android/widget/RemoteViews.java144
-rw-r--r--core/java/android/widget/RemoteViewsService.java25
-rw-r--r--core/java/android/widget/ScrollView.java17
-rw-r--r--core/java/android/window/flags/responsible_apis.aconfig2
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java125
-rw-r--r--core/java/com/android/internal/inputmethod/IImeTracker.aidl25
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethod.aidl8
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl8
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java32
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java31
-rw-r--r--core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java106
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java8
-rw-r--r--core/java/com/android/internal/protolog/LegacyProtoLogImpl.java2
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogImpl.java2
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl13
-rw-r--r--core/java/com/android/internal/widget/IRemoteViewsFactory.aidl2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java2
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/LayoutlibLoader.cpp80
-rw-r--r--core/jni/android_view_MotionEvent.cpp13
-rw-r--r--core/proto/android/hardware/sensorprivacy.proto4
-rw-r--r--core/proto/android/server/inputmethod/inputmethodmanagerservice.proto2
-rw-r--r--core/proto/android/server/vibrator/vibratormanagerservice.proto25
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/values-af/strings.xml3
-rw-r--r--core/res/res/values-am/strings.xml3
-rw-r--r--core/res/res/values-ar/strings.xml3
-rw-r--r--core/res/res/values-as/strings.xml3
-rw-r--r--core/res/res/values-az/strings.xml3
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml3
-rw-r--r--core/res/res/values-be/strings.xml3
-rw-r--r--core/res/res/values-bg/strings.xml3
-rw-r--r--core/res/res/values-bn/strings.xml3
-rw-r--r--core/res/res/values-bs/strings.xml3
-rw-r--r--core/res/res/values-ca/strings.xml3
-rw-r--r--core/res/res/values-cs/strings.xml3
-rw-r--r--core/res/res/values-da/strings.xml5
-rw-r--r--core/res/res/values-de/strings.xml7
-rw-r--r--core/res/res/values-el/strings.xml3
-rw-r--r--core/res/res/values-en-rAU/strings.xml3
-rw-r--r--core/res/res/values-en-rCA/strings.xml3
-rw-r--r--core/res/res/values-en-rGB/strings.xml3
-rw-r--r--core/res/res/values-en-rIN/strings.xml3
-rw-r--r--core/res/res/values-en-rXC/strings.xml3
-rw-r--r--core/res/res/values-es-rUS/strings.xml5
-rw-r--r--core/res/res/values-es/strings.xml3
-rw-r--r--core/res/res/values-et/strings.xml3
-rw-r--r--core/res/res/values-eu/strings.xml3
-rw-r--r--core/res/res/values-fa/strings.xml7
-rw-r--r--core/res/res/values-fi/strings.xml3
-rw-r--r--core/res/res/values-fr-rCA/strings.xml3
-rw-r--r--core/res/res/values-fr/strings.xml3
-rw-r--r--core/res/res/values-gl/strings.xml3
-rw-r--r--core/res/res/values-gu/strings.xml3
-rw-r--r--core/res/res/values-hi/strings.xml7
-rw-r--r--core/res/res/values-hr/strings.xml3
-rw-r--r--core/res/res/values-hu/strings.xml3
-rw-r--r--core/res/res/values-hy/strings.xml3
-rw-r--r--core/res/res/values-in/strings.xml3
-rw-r--r--core/res/res/values-is/strings.xml3
-rw-r--r--core/res/res/values-it/strings.xml3
-rw-r--r--core/res/res/values-iw/strings.xml3
-rw-r--r--core/res/res/values-ja/strings.xml3
-rw-r--r--core/res/res/values-ka/strings.xml3
-rw-r--r--core/res/res/values-kk/strings.xml3
-rw-r--r--core/res/res/values-km/strings.xml3
-rw-r--r--core/res/res/values-kn/strings.xml3
-rw-r--r--core/res/res/values-ko/strings.xml3
-rw-r--r--core/res/res/values-ky/strings.xml3
-rw-r--r--core/res/res/values-lo/strings.xml3
-rw-r--r--core/res/res/values-lt/strings.xml3
-rw-r--r--core/res/res/values-lv/strings.xml3
-rw-r--r--core/res/res/values-mk/strings.xml3
-rw-r--r--core/res/res/values-ml/strings.xml3
-rw-r--r--core/res/res/values-mn/strings.xml3
-rw-r--r--core/res/res/values-mr/strings.xml3
-rw-r--r--core/res/res/values-ms/strings.xml3
-rw-r--r--core/res/res/values-my/strings.xml3
-rw-r--r--core/res/res/values-nb/strings.xml3
-rw-r--r--core/res/res/values-ne/strings.xml3
-rw-r--r--core/res/res/values-nl/strings.xml5
-rw-r--r--core/res/res/values-or/strings.xml3
-rw-r--r--core/res/res/values-pa/strings.xml3
-rw-r--r--core/res/res/values-pl/strings.xml3
-rw-r--r--core/res/res/values-pt-rBR/strings.xml3
-rw-r--r--core/res/res/values-pt-rPT/strings.xml3
-rw-r--r--core/res/res/values-pt/strings.xml3
-rw-r--r--core/res/res/values-ro/strings.xml3
-rw-r--r--core/res/res/values-ru/strings.xml3
-rw-r--r--core/res/res/values-si/strings.xml3
-rw-r--r--core/res/res/values-sk/strings.xml3
-rw-r--r--core/res/res/values-sl/strings.xml3
-rw-r--r--core/res/res/values-sq/strings.xml3
-rw-r--r--core/res/res/values-sr/strings.xml3
-rw-r--r--core/res/res/values-sv/strings.xml5
-rw-r--r--core/res/res/values-sw/strings.xml3
-rw-r--r--core/res/res/values-ta/strings.xml3
-rw-r--r--core/res/res/values-te/strings.xml3
-rw-r--r--core/res/res/values-th/strings.xml3
-rw-r--r--core/res/res/values-tl/strings.xml3
-rw-r--r--core/res/res/values-tr/strings.xml3
-rw-r--r--core/res/res/values-uk/strings.xml3
-rw-r--r--core/res/res/values-ur/strings.xml3
-rw-r--r--core/res/res/values-uz/strings.xml3
-rw-r--r--core/res/res/values-vi/strings.xml3
-rw-r--r--core/res/res/values-zh-rCN/strings.xml3
-rw-r--r--core/res/res/values-zh-rHK/strings.xml3
-rw-r--r--core/res/res/values-zh-rTW/strings.xml3
-rw-r--r--core/res/res/values-zu/strings.xml3
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--core/res/res/values/config.xml43
-rw-r--r--core/res/res/values/config_telephony.xml7
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/symbols.xml9
-rw-r--r--core/res/res/xml/bookmarks.xml4
-rw-r--r--core/res/res/xml/sms_short_codes.xml13
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java2
-rw-r--r--core/tests/ConnectivityManagerTest/Android.bp5
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java3
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java4
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java3
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java3
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java10
-rw-r--r--core/tests/bandwidthtests/Android.bp5
-rw-r--r--core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java3
-rw-r--r--core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java45
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/res/layout/activity_horizontal_scroll_view.xml241
-rw-r--r--core/tests/coretests/res/layout/activity_scroll_view.xml241
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java24
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java129
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java10
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java34
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java9
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java2
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java23
-rw-r--r--core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java2
-rw-r--r--core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java3
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesManagerTest.java132
-rw-r--r--core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java3
-rw-r--r--core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java4
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerTest.java4
-rw-r--r--core/tests/coretests/src/android/os/WorkDurationUnitTest.java4
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java30
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java24
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java2
-rw-r--r--core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java20
-rw-r--r--core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java53
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java2
-rw-r--r--core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java52
-rw-r--r--core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java61
-rw-r--r--core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java2
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java6
-rw-r--r--core/tests/systemproperties/Android.bp2
-rw-r--r--core/tests/systemproperties/src/android/os/SystemPropertiesTest.java3
-rw-r--r--data/etc/Android.bp6
-rw-r--r--data/etc/CleanSpec.mk2
-rw-r--r--data/etc/package-shareduid-allowlist.xml35
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--data/etc/services.core.protolog.json2
-rw-r--r--data/fonts/font_fallback.xml8
-rw-r--r--data/fonts/font_fallback_cjkvf.xml8
-rw-r--r--data/fonts/fonts.xml8
-rw-r--r--data/fonts/fonts_cjkvf.xml8
-rw-r--r--graphics/java/android/graphics/Matrix44.java14
-rw-r--r--graphics/java/android/graphics/text/MeasuredText.java50
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreProvider.java22
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java2
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java7
-rw-r--r--keystore/java/android/security/keystore/KeyStoreCryptoOperation.java8
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java3
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java7
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java3
-rw-r--r--keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java5
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java144
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java3
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java12
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java92
-rw-r--r--libs/WindowManager/Shell/Android.bp73
-rw-r--r--libs/WindowManager/Shell/multivalentTests/Android.bp97
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml26
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml20
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml3
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt150
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java181
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java225
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt26
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt25
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java55
-rw-r--r--libs/androidfw/ApkAssets.cpp5
-rw-r--r--libs/androidfw/Idmap.cpp15
-rw-r--r--libs/androidfw/ResourceTypes.cpp25
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h10
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h4
-rw-r--r--libs/androidfw/include/androidfw/misc.h2
-rw-r--r--libs/androidfw/misc.cpp20
-rw-r--r--libs/hostgraphics/ADisplay.cpp159
-rw-r--r--libs/hostgraphics/Android.bp12
-rw-r--r--libs/hwui/Android.bp23
-rw-r--r--libs/hwui/FrameInfoVisualizer.cpp2
-rw-r--r--libs/hwui/JankTracker.cpp7
-rw-r--r--libs/hwui/RenderProperties.h27
-rw-r--r--libs/hwui/VectorDrawable.cpp8
-rw-r--r--media/java/android/media/AudioManager.java4
-rw-r--r--media/java/android/media/RingtoneSelection.java742
-rw-r--r--media/java/android/media/metrics/PlaybackSession.java20
-rw-r--r--media/jni/android_media_MediaCodec.cpp13
-rw-r--r--media/lib/tvremote/tests/Android.bp1
-rw-r--r--media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java3
-rw-r--r--media/mca/tests/Android.bp5
-rw-r--r--media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java15
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java9
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java13
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java15
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioEffectTest.java22
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java10
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java10
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java20
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java22
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java20
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java22
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java20
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java18
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/SimTonesTest.java9
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraFunctionalTest.java19
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraPairwiseTest.java9
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java16
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java23
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java25
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java63
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java22
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java9
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java8
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java23
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/template/AudioTestHarnessTemplateAndroidTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java20
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetDurationStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoHeightStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoWidthStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerIsPlayingStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java5
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerPauseStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerResetStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSeekToStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetAudioStreamTypeStateUnitTest.java5
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetLoopingStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetVolumeStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStartStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStopStateUnitTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java5
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java3
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java7
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java3
-rw-r--r--media/tests/projection/Android.bp1
-rw-r--r--native/android/Android.bp1
-rw-r--r--nfc/api/current.txt2
-rw-r--r--nfc/java/android/nfc/cardemulation/PollingFrame.java13
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java50
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java51
-rw-r--r--packages/CredentialManager/res/values-af/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-am/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ar/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-as/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-az/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-b+sr+Latn/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-be/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-bg/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-bn/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-bs/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ca/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-cs/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-da/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-de/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-el/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-en-rAU/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-en-rCA/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-en-rGB/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-en-rIN/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-en-rXC/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-es-rUS/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-es/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-et/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-eu/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-fa/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-fi/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-fr/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-gl/strings.xml8
-rw-r--r--packages/CredentialManager/res/values-gu/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-hi/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-hr/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-hu/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-hy/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-in/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-is/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-it/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-iw/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ja/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ka/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-kk/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-km/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-kn/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ko/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ky/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-lo/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-lt/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-lv/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-mk/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ml/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-mn/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-mr/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ms/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-my/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-nb/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ne/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-nl/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-or/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-pa/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-pl/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-pt-rBR/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-pt-rPT/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-pt/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ro/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ru/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-si/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-sk/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-sl/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-sq/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-sr/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-sv/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-sw/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ta/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-te/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-th/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-tl/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-tr/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-uk/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-ur/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-uz/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-vi/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-zh-rHK/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-zh-rTW/strings.xml6
-rw-r--r--packages/CredentialManager/res/values-zu/strings.xml6
-rw-r--r--packages/CredentialManager/res/values/dimens.xml2
-rw-r--r--packages/CredentialManager/res/values/strings.xml4
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt19
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt9
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt27
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt51
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt13
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt51
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt106
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt38
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt273
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt45
-rw-r--r--packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt11
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt25
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt12
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt8
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt17
-rw-r--r--packages/EasterEgg/Android.bp15
-rw-r--r--packages/EasterEgg/easter_egg_flags.aconfig8
-rw-r--r--packages/EasterEgg/src/com/android/egg/landroid/Universe.kt4
-rw-r--r--packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt21
-rw-r--r--packages/PackageInstaller/res/layout/install_content_view.xml192
-rw-r--r--packages/PackageInstaller/res/layout/uninstall_content_view.xml53
-rw-r--r--packages/PackageInstaller/res/values-el/strings.xml2
-rw-r--r--packages/PackageInstaller/res/values-fr/strings.xml6
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java5
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java8
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt6
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java2
-rw-r--r--packages/SettingsLib/DataStore/Android.bp16
-rw-r--r--packages/SettingsLib/DataStore/AndroidManifest.xml6
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreContext.kt72
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt69
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt140
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt153
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt183
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/ObservableBackupRestoreStorage.kt (renamed from packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt)14
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt121
-rw-r--r--packages/SettingsLib/DataStore/tests/Android.bp24
-rw-r--r--packages/SettingsLib/DataStore/tests/AndroidManifest.xml6
-rw-r--r--packages/SettingsLib/DataStore/tests/config/robolectric.properties1
-rw-r--r--packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt109
-rw-r--r--packages/SettingsLib/OWNERS1
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt12
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java36
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java23
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java19
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java447
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt506
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java3
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java50
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java27
-rw-r--r--packages/SettingsProvider/res/xml/bookmarks.xml6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java56
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java24
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java62
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig8
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java33
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/Shell/tests/src/com/android/shell/UtilitiesTest.java6
-rw-r--r--packages/SystemUI/Android.bp79
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java22
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java65
-rw-r--r--packages/SystemUI/aconfig/predictive_back.aconfig8
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig63
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt125
-rw-r--r--packages/SystemUI/compose/core/TEST_MAPPING11
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt132
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/BouncerSceneModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/ShadeSceneModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt250
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt77
-rw-r--r--packages/SystemUI/compose/features/Android.bp48
-rw-r--r--packages/SystemUI/compose/features/AndroidManifest.xml21
-rw-r--r--packages/SystemUI/compose/features/TEST_MAPPING26
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt86
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt78
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt68
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt33
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt25
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt20
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt72
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt19
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt9
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt9
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt81
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeUnawareExtensions.kt41
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt25
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt33
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt43
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt11
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt16
-rw-r--r--packages/SystemUI/compose/features/tests/AndroidManifest.xml54
-rw-r--r--packages/SystemUI/compose/scene/TEST_MAPPING11
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt66
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt125
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt104
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt78
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt75
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt104
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt53
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt44
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt227
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt96
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt190
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt99
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt62
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt237
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt72
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt2
-rw-r--r--packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml322
-rw-r--r--packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml304
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml226
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_layout.xml2
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml3
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf.xml6
-rw-r--r--packages/SystemUI/res/layout/scene_window_root.xml2
-rw-r--r--packages/SystemUI/res/values-af/strings.xml10
-rw-r--r--packages/SystemUI/res/values-af/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-am/strings.xml10
-rw-r--r--packages/SystemUI/res/values-am/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ar/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-as/strings.xml10
-rw-r--r--packages/SystemUI/res/values-as/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-az/strings.xml10
-rw-r--r--packages/SystemUI/res/values-az/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml10
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-be/strings.xml10
-rw-r--r--packages/SystemUI/res/values-be/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml10
-rw-r--r--packages/SystemUI/res/values-bg/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml10
-rw-r--r--packages/SystemUI/res/values-bn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml10
-rw-r--r--packages/SystemUI/res/values-bs/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ca/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml10
-rw-r--r--packages/SystemUI/res/values-cs/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-da/strings.xml10
-rw-r--r--packages/SystemUI/res/values-da/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-de/strings.xml10
-rw-r--r--packages/SystemUI/res/values-de/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-el/strings.xml10
-rw-r--r--packages/SystemUI/res/values-el/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml7
-rw-r--r--packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-es/strings.xml12
-rw-r--r--packages/SystemUI/res/values-es/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-et/strings.xml10
-rw-r--r--packages/SystemUI/res/values-et/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml16
-rw-r--r--packages/SystemUI/res/values-eu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml10
-rw-r--r--packages/SystemUI/res/values-fa/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml10
-rw-r--r--packages/SystemUI/res/values-fi/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml10
-rw-r--r--packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-fr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-gl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml10
-rw-r--r--packages/SystemUI/res/values-gu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml14
-rw-r--r--packages/SystemUI/res/values-hi/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-hr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml10
-rw-r--r--packages/SystemUI/res/values-hu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml10
-rw-r--r--packages/SystemUI/res/values-hy/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-in/strings.xml10
-rw-r--r--packages/SystemUI/res/values-in/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-is/strings.xml10
-rw-r--r--packages/SystemUI/res/values-is/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-it/strings.xml14
-rw-r--r--packages/SystemUI/res/values-it/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml10
-rw-r--r--packages/SystemUI/res/values-iw/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ja/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ka/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml10
-rw-r--r--packages/SystemUI/res/values-kk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-km/strings.xml10
-rw-r--r--packages/SystemUI/res/values-km/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml10
-rw-r--r--packages/SystemUI/res/values-kn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ko/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ky/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml7
-rw-r--r--packages/SystemUI/res/values-lo/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml10
-rw-r--r--packages/SystemUI/res/values-lt/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml12
-rw-r--r--packages/SystemUI/res/values-lv/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml10
-rw-r--r--packages/SystemUI/res/values-mk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ml/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml10
-rw-r--r--packages/SystemUI/res/values-mn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-mr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ms/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-my/strings.xml10
-rw-r--r--packages/SystemUI/res/values-my/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml10
-rw-r--r--packages/SystemUI/res/values-nb/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ne/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml14
-rw-r--r--packages/SystemUI/res/values-nl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-or/strings.xml12
-rw-r--r--packages/SystemUI/res/values-or/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml10
-rw-r--r--packages/SystemUI/res/values-pa/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-pl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml10
-rw-r--r--packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml7
-rw-r--r--packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml10
-rw-r--r--packages/SystemUI/res/values-pt/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ro/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ru/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-si/strings.xml10
-rw-r--r--packages/SystemUI/res/values-si/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sq/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sv/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sw/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ta/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-te/strings.xml10
-rw-r--r--packages/SystemUI/res/values-te/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-th/strings.xml10
-rw-r--r--packages/SystemUI/res/values-th/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-tl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-tr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml10
-rw-r--r--packages/SystemUI/res/values-uk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ur/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml10
-rw-r--r--packages/SystemUI/res/values-uz/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml10
-rw-r--r--packages/SystemUI/res/values-vi/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml10
-rw-r--r--packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml10
-rw-r--r--packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml10
-rw-r--r--packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml10
-rw-r--r--packages/SystemUI/res/values-zu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/res/values/styles.xml4
-rw-r--r--packages/SystemUI/res/values/tiles_states_strings.xml10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt246
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java45
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java27
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt153
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt130
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt69
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraRotationModule.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepository.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalScenes.kt (renamed from packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt)20
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt115
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt130
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt327
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepository.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt169
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt130
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt87
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt123
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt147
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialogDelegate.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/model/RotationLockTileModel.kt (renamed from packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKey.kt)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt107
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt (renamed from packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt)22
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java (renamed from packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java)61
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt159
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java (renamed from packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java)87
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java158
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java124
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java295
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationViewFlipperFactory.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTaskLogger.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModel.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationContentAlphaOptimization.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationViewFlipperPausing.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java177
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractor.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt132
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt286
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt312
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt163
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt224
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt342
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt319
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt129
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt123
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt121
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt85
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt)11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepository.kt37
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepository.kt36
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt20
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt101
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepositoryKosmos.kt (renamed from packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt)6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt40
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt40
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt38
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt40
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModelKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeActivityTaskManager.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt)20
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeMediaProjectionManager.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt)13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt64
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/rotation/RotationLockTileKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt16
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt36
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorKosmos.kt (renamed from packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt)24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DevicePostureControllerKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt3
-rw-r--r--proto/src/criticalevents/critical_event_log.proto6
-rw-r--r--ravenwood/Android.bp33
-rw-r--r--ravenwood/framework-src/android/ravenwood/example/BlueManager.java34
-rw-r--r--ravenwood/framework-src/android/ravenwood/example/RedManager.java34
-rw-r--r--ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java54
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java20
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java51
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java4
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java3
-rw-r--r--ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesDependenciesTest.java54
-rw-r--r--ravenwood/test-authors.md18
-rw-r--r--sax/tests/saxtests/Android.bp5
-rw-r--r--sax/tests/saxtests/src/android/sax/SafeSaxTest.java16
-rw-r--r--services/accessibility/accessibility.aconfig7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java84
-rw-r--r--services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java59
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/OneFingerPanningSettingsProvider.java104
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java15
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java22
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java2
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java82
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java37
-rw-r--r--services/companion/java/com/android/server/companion/AssociationStore.java144
-rw-r--r--services/companion/java/com/android/server/companion/BackupRestoreProcessor.java11
-rw-r--r--services/companion/java/com/android/server/companion/CompanionApplicationController.java32
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java111
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java10
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationDiskStore.java (renamed from services/companion/java/com/android/server/companion/PersistentDataStore.java)69
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java (renamed from services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java)128
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationRevokeProcessor.java (renamed from services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java)69
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationStore.java (renamed from services/companion/java/com/android/server/companion/AssociationStoreImpl.java)196
-rw-r--r--services/companion/java/com/android/server/companion/association/InactiveAssociationsRemovalService.java (renamed from services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java)17
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java8
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java10
-rw-r--r--services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java10
-rw-r--r--services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java6
-rw-r--r--services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java133
-rw-r--r--services/companion/java/com/android/server/companion/presence/ObservableUuid.java (renamed from services/companion/java/com/android/server/companion/ObservableUuid.java)2
-rw-r--r--services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java (renamed from services/companion/java/com/android/server/companion/ObservableUuidStore.java)22
-rw-r--r--services/companion/java/com/android/server/companion/presence/Utils.java49
-rw-r--r--services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java2
-rw-r--r--services/companion/java/com/android/server/companion/utils/AssociationUtils.java42
-rw-r--r--services/companion/java/com/android/server/companion/utils/DataStoreUtils.java (renamed from services/companion/java/com/android/server/companion/DataStoreUtils.java)4
-rw-r--r--services/companion/java/com/android/server/companion/utils/MetricUtils.java (renamed from services/companion/java/com/android/server/companion/MetricUtils.java)14
-rw-r--r--services/companion/java/com/android/server/companion/utils/PackageUtils.java (renamed from services/companion/java/com/android/server/companion/PackageUtils.java)48
-rw-r--r--services/companion/java/com/android/server/companion/utils/PermissionsUtils.java (renamed from services/companion/java/com/android/server/companion/PermissionsUtils.java)61
-rw-r--r--services/companion/java/com/android/server/companion/utils/RolesUtils.java (renamed from services/companion/java/com/android/server/companion/RolesUtils.java)54
-rw-r--r--services/companion/java/com/android/server/companion/utils/Utils.java (renamed from services/companion/java/com/android/server/companion/Utils.java)26
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java6
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java4
-rw-r--r--services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java103
-rw-r--r--services/core/java/com/android/server/SystemConfig.java31
-rw-r--r--services/core/java/com/android/server/SystemService.java40
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java6
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java36
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java354
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java5
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig14
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java8
-rw-r--r--services/core/java/com/android/server/biometrics/log/ALSProbe.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java14
-rw-r--r--services/core/java/com/android/server/connectivity/TEST_MAPPING7
-rw-r--r--services/core/java/com/android/server/criticalevents/CriticalEventLog.java10
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java23
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessEvent.java12
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java10
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java12
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java6
-rw-r--r--services/core/java/com/android/server/flags/services.aconfig8
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java7
-rw-r--r--services/core/java/com/android/server/input/KeyboardLayoutManager.java115
-rw-r--r--services/core/java/com/android/server/input/KeyboardMetricsCollector.java93
-rw-r--r--services/core/java/com/android/server/input/debug/FocusEventDebugView.java3
-rw-r--r--services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java47
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeBindingState.java109
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeTrackerService.java32
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java17
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java14
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java429
-rw-r--r--services/core/java/com/android/server/inputmethod/ZeroJankProxy.java15
-rw-r--r--services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java24
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java50
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java1
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java44
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java18
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java71
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java46
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java41
-rw-r--r--services/core/java/com/android/server/net/TEST_MAPPING5
-rw-r--r--services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java4
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java51
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java11
-rw-r--r--services/core/java/com/android/server/notification/ZenModeEventLogger.java7
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java1
-rw-r--r--services/core/java/com/android/server/om/IdmapDaemon.java30
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java56
-rw-r--r--services/core/java/com/android/server/os/BugreportManagerServiceImpl.java89
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java27
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java18
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java24
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java15
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java28
-rw-r--r--services/core/java/com/android/server/pm/Settings.java14
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java5
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java3
-rw-r--r--services/core/java/com/android/server/policy/ModifierShortcutManager.java106
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java24
-rw-r--r--services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java3
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java2
-rw-r--r--services/core/java/com/android/server/power/Android.bp1
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java122
-rw-r--r--services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java182
-rw-r--r--services/core/java/com/android/server/vibrator/HalVibration.java28
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java8
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java23
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java52
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationScaler.java167
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java80
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorControlService.java277
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java5
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorControllerHolder.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java205
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperCropper.java41
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperDataParser.java2
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java17
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java21
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java65
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java25
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java47
-rw-r--r--services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java28
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java23
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java4
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java47
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java357
-rw-r--r--services/core/java/com/android/server/wm/ClientLifecycleManager.java30
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java8
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java6
-rw-r--r--services/core/java/com/android/server/wm/InsetsControlTarget.java8
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java2
-rw-r--r--services/core/java/com/android/server/wm/Session.java8
-rw-r--r--services/core/java/com/android/server/wm/Task.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java5
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java42
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java54
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java10
-rw-r--r--services/core/jni/com_android_server_accessibility_BrailleDisplayConnection.cpp14
-rw-r--r--services/core/jni/com_android_server_hint_HintManagerService.cpp21
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerUi.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java7
-rw-r--r--services/credentials/java/com/android/server/credentials/RequestSession.java40
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java14
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java16
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java8
-rw-r--r--services/fakes/java/com/android/server/example/BlueManagerService.java44
-rw-r--r--services/fakes/java/com/android/server/example/RedManagerService.java64
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java5
-rw-r--r--services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java7
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/AppOpService.kt338
-rw-r--r--services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt105
-rw-r--r--services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt120
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt84
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt14
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt138
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java70
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java27
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java9
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt18
-rw-r--r--services/tests/VpnTests/Android.bp39
-rw-r--r--services/tests/VpnTests/AndroidManifest.xml28
-rw-r--r--services/tests/VpnTests/AndroidTest.xml28
-rw-r--r--services/tests/VpnTests/OWNERS2
-rw-r--r--services/tests/VpnTests/java/android/net/Ikev2VpnProfileTest.java563
-rw-r--r--services/tests/VpnTests/java/android/net/VpnManagerTest.java144
-rw-r--r--services/tests/VpnTests/java/com/android/internal/net/VpnProfileTest.java308
-rw-r--r--services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java400
-rw-r--r--services/tests/VpnTests/java/com/android/server/VpnTestBase.java97
-rw-r--r--services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java3293
-rw-r--r--services/tests/VpnTests/java/com/android/server/net/LockdownVpnTrackerTest.java (renamed from services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java60
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java14
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java26
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java15
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java25
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java53
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java237
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp (renamed from packages/SystemUI/compose/features/tests/Android.bp)44
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml34
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java640
-rw-r--r--services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java274
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/OneFingerPanningSettingsProviderTest.java153
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java305
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java127
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java66
-rw-r--r--services/tests/uiservicestests/Android.bp2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java24
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java57
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java88
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java13
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java7
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java2
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java12
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java125
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java26
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java2
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java267
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java64
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java14
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java42
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java2
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java6
-rw-r--r--services/tests/voiceinteractiontests/Android.bp1
-rw-r--r--services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java9
-rw-r--r--services/tests/wmtests/AndroidManifest.xml2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java46
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java7
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java100
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java31
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java35
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java38
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java13
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java14
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java6
-rw-r--r--telephony/java/android/telephony/TelephonyFrameworkInitializer.java27
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java6
-rw-r--r--test-runner/src/android/test/InstrumentationTestRunner.java24
-rw-r--r--test-runner/src/android/test/suitebuilder/TestPredicates.java2
-rw-r--r--test-runner/tests/src/android/test/AndroidTestRunnerTest.java8
-rw-r--r--tests/CoreTests/android/Android.bp5
-rw-r--r--tests/CoreTests/android/core/RequestAPITest.java3
-rw-r--r--tests/FlickerTests/Android.bp13
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt1
-rw-r--r--tests/FlickerTests/libs/window-extensions-release.aarbin21364 -> 0 bytes
-rw-r--r--tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java20
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt78
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt17
-rw-r--r--tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java2
-rw-r--r--tests/SurfaceComposition/Android.bp5
-rw-r--r--tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java3
-rw-r--r--tests/graphics/HwAccelerationTest/jni/native-lib.cpp2
-rw-r--r--tests/permission/Android.bp1
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java3
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java3
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java3
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java3
-rw-r--r--tests/testables/tests/src/android/testing/TestableLooperTest.java3
-rw-r--r--tests/testables/tests/src/android/testing/TestableResourcesTest.java2
-rw-r--r--tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java2
-rw-r--r--tests/utils/testutils/tests/Android.bp1
-rw-r--r--tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java3
-rw-r--r--tools/aapt2/Android.bp7
-rw-r--r--tools/aapt2/Android.mk4
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt4
-rwxr-xr-xtools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py1
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/Constants.kt2
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt9
-rw-r--r--tools/streaming_proto/Android.bp56
-rw-r--r--tools/streaming_proto/java/java_proto_stream_code_generator.cpp339
-rw-r--r--tools/streaming_proto/java/java_proto_stream_code_generator.h29
-rw-r--r--tools/streaming_proto/java/main.cpp278
-rw-r--r--tools/streaming_proto/test/integration/imported.proto (renamed from tools/streaming_proto/test/imported.proto)0
-rw-r--r--tools/streaming_proto/test/integration/src/com/android/streaming_proto_test/Main.java (renamed from packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/captioning/CaptioningModule.kt)10
-rw-r--r--tools/streaming_proto/test/integration/test.proto (renamed from tools/streaming_proto/test/test.proto)2
-rw-r--r--tools/streaming_proto/test/src/com/android/streaming_proto_test/Main.java7
-rw-r--r--tools/streaming_proto/test/unit/streaming_proto_java.cpp191
-rw-r--r--wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java22
1899 files changed, 37669 insertions, 13668 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 20471682dd2e..a80194cf53d2 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -310,6 +310,8 @@ java_aconfig_library {
aconfig_declarations {
name: "android.os.flags-aconfig",
package: "android.os",
+ exportable: true,
+ container: "system",
srcs: ["core/java/android/os/*.aconfig"],
}
@@ -326,6 +328,24 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.os.flags-aconfig-java-export",
+ aconfig_declarations: "android.os.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+}
+
+cc_aconfig_library {
+ name: "android.os.flags-aconfig-cc",
+ aconfig_declarations: "android.os.flags-aconfig",
+}
+
+cc_aconfig_library {
+ name: "android.os.flags-aconfig-cc-test",
+ aconfig_declarations: "android.os.flags-aconfig",
+ mode: "test",
+}
+
// VirtualDeviceManager
cc_aconfig_library {
name: "android.companion.virtualdevice.flags-aconfig-cc",
@@ -483,6 +503,13 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.content.res.flags-aconfig-java-host",
+ aconfig_declarations: "android.content.res.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Media BetterTogether
aconfig_declarations {
name: "com.android.media.flags.bettertogether-aconfig",
diff --git a/Android.bp b/Android.bp
index 5ada10d19f5d..8d7ab983593d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -386,6 +386,7 @@ java_defaults {
// TODO(b/120066492): remove gps_debug and protolog.conf.json when the build
// system propagates "required" properly.
"gps_debug.conf",
+ "protolog.conf.json.gz",
"core.protolog.pb",
"framework-res",
// any install dependencies should go into framework-minus-apex-install-dependencies
diff --git a/Ravenwood.bp b/Ravenwood.bp
index c73e04896173..f43c37bf637d 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -149,6 +149,7 @@ java_library {
installable: false,
srcs: [":services.fakes-sources"],
libs: [
+ "ravenwood-framework",
"services.core.ravenwood",
],
jarjar_rules: ":ravenwood-services-jarjar-rules",
@@ -204,7 +205,10 @@ android_ravenwood_libgroup {
// Provide runtime versions of utils linked in below
"junit",
"truth",
+ "flag-junit",
+ "ravenwood-framework",
"ravenwood-junit-impl",
+ "ravenwood-junit-impl-flag",
"mockito-ravenwood-prebuilt",
"inline-mockito-ravenwood-prebuilt",
],
@@ -218,6 +222,8 @@ android_ravenwood_libgroup {
libs: [
"junit",
"truth",
+ "flag-junit",
+ "ravenwood-framework",
"ravenwood-junit",
"mockito-ravenwood-prebuilt",
"inline-mockito-ravenwood-prebuilt",
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
index d14e93e553f6..80a9c06b549e 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
@@ -27,8 +27,8 @@ import android.perftests.utils.BenchmarkState;
import android.perftests.utils.BitmapUtils;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
diff --git a/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
index aa47e0a29695..80cd86cf9a5b 100644
--- a/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
index 97ab6c7cd6b4..2f6c37832d04 100644
--- a/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
index bb452d394d47..d17add767257 100644
--- a/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
index ff6d46f6db7f..3a57db8f323f 100644
--- a/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
index e0c12dd660e2..3fb3bc8c0ff2 100644
--- a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
index 04ef09e4b682..2a1b5d1cc6ce 100644
--- a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
index 4ae88b88b090..5f599ea85eb4 100644
--- a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Assert;
diff --git a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
index 5e73916d5f5b..ea249848daad 100644
--- a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
index 3ebaa4cd0bfa..82247dcee772 100644
--- a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
index da94ae118900..0bebf04c6897 100644
--- a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
index 9446d99c959d..55c1027e1add 100644
--- a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
index be2a7e97f775..da60a7773528 100644
--- a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
index ca9977974a8a..6d9d0c92ff25 100644
--- a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
index 8496fbecb6bd..09b09771e9de 100644
--- a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
index bb794249e9f4..ba21ed33bdb2 100644
--- a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
index 05a3e1201a00..293752ee5dd1 100644
--- a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
index 65a2fdbae304..528b751d1551 100644
--- a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
index 4f5c54d6a847..1f301acd1dc6 100644
--- a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
index 08ad92694013..4268325f8c64 100644
--- a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
index 20f1309bd8e6..6363e9c3ef21 100644
--- a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
index 7e71976fdc9d..cb3d3acb337f 100644
--- a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
index b1b594d64324..5be8ee6e67e2 100644
--- a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
@@ -18,7 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java
index 412cb5acbf8b..a37b89ddf033 100644
--- a/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java
@@ -18,9 +18,9 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Xml;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import libcore.util.XmlObjectFactory;
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index 3a45d4045d62..ed669beae1ce 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -18,7 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
index 2e89518ec9fb..d239a054fac7 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java
index d38d5197b937..487295c03c0e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java
index cc56868468e5..adc5d8c2bb3e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java
index 662694b1b5d1..286d70339c5f 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
index 2c0473eda830..d6462024a380 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
index 6a2ce5847daa..b887f4033462 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
index b7b7e83f147c..e4eaf12e9dcb 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
index 9ac36d076c7b..cb2438e519da 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
index 5dd9d6e2bc2c..9ee927cfc353 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
index 0a598998bced..e4a4db739235 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
index 8da13a9b7f91..858c101fc33e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
index 048c50f044c8..a2fb7d7f83d8 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java
index b753006e193e..2047444a7f52 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.icu.lang.UCharacter;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java
index 1d33fcb250b9..4ce8b41de403 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java
index 35730ec753f1..6a7ec1ad62e9 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
index 42b058815bfe..238c028fa0cf 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java
index 6728e73d0c4b..7e5566055fb4 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
index 69197c3325f4..100798a7957b 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java
index 4dba1391438c..b6784a8d3867 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java
index f3eddab0e77c..52f98738481f 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java
index 2bf04180fa79..610542061107 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java
@@ -17,8 +17,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java
index c3320a4e5a20..fae74a5e8620 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java
index 7c52ac424a2a..2915363786f3 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java
index d13335906ec6..dd7e5cc1057b 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java
@@ -18,15 +18,14 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-
import java.security.AccessController;
import java.security.PrivilegedAction;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java
index 38904af7edd7..e034a47e79d2 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
index 839120336697..fe1b599cc5ad 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
index 7712aeef4161..ecbfc7169945 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java
index 783136a5b3a2..0c14d64c27a9 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java
index a995f5caa183..7d7d83b3fdbf 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java
index 94c4f0807cbc..08dda5314eac 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java
index c60930f99682..a09ad809a6a2 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java
index abcc972b0ac3..be22814ef8f4 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java
index c9f06166e33b..4337c903ecd6 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java
index 78f744c1b938..1b6c5026e1f6 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java
index 5129fcbfb247..0aa854ecfa80 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
index 80c448732b59..9b3d7a044720 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
index c9b0cbe1bedb..1a9e19aeb78d 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java
index 4c2d7fb7e663..a8a704c09d6d 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
index 2dc947a613d2..6da9666db23b 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
index d9d4bb5d0ae1..060d18fb3de3 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java
index dae185e1209c..7cb3b2283779 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java
index 5ff2b225d64f..272b45a3affe 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
index 48450b4616e6..c3a09662fd1f 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java
index 21ccba5cc2e7..2ac56bed1910 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java
index f7bcf12c858e..7ad0141c8471 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java
index d8bff4cc20d1..c7b6cb5a190c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java
index 2542df90896f..44e5f227b00a 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java
index b06662ccd9b5..6e00b10838a6 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java
index 694d609cd950..5a9b5c36d0d3 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import libcore.java.security.TestKeyStore;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java
index bdbbcb0da006..6d48cf26c971 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
index 5ad62dedcae7..86416291f26d 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java
index 1ec22d217109..afd1191a07fa 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
index a9a0788f6136..6c261332aa99 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
@@ -17,7 +17,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java
index c25b0ce6d7d9..274b51f6fae4 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java
index eeccb5bd64f4..b4c427beac65 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
index 10fa8b93acaf..2235cc5611a4 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java
index 6854c0de9387..9ab50005e62a 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import junit.framework.Assert;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java
index 79ff646bbfff..b1e749cc538c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java
index 8dbf9f58fbf8..9e5759171a6f 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
index 36db014b75a5..a80514c72e95 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
index 5b4423a32831..78ae3952719b 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
index 4d5c792295b9..73911c71c351 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java
index 2bb25ac56694..1539271c2b3c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
index c004d959b9b3..0d5e62b4268c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
index 15516fc1c51c..ecdf809f1610 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java
index f2565550c6c5..2b2a6b5727de 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java
index 8274512e1501..6eb8fccf5b2a 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
index ae1e8bce42ac..288c646ec67d 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
@@ -18,7 +18,8 @@ package android.libcore.regression;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java
index e7bb8f84d671..003c957d894f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java
index 5bac46af4630..4f216181d5e3 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java
index 1005a70e96e0..210014ad3f6e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java
index 5224ad3b5911..22c68273bf12 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java
index 06696ef0773b..5b391091253b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java
index a784c524a4c5..883e8a76586b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java
index 4ce0078ff1c1..50bc85c31280 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java
index 587e201c4e17..13fa2bf7d230 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java
index e06b53434438..85c9bae9a9d9 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java
index 0fd16a02e22c..2b8f430440f5 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java
index 7ad42d057588..246fa43d0dca 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java
index 76e1f47251cf..d12ffae25c5e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java
index b4b78408a45f..5ced1157cb73 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java
index 09ed167cef35..b955d506fe85 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java
index 920d2e4767e5..601ff3461f5b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java
index 55ed789d1227..0e567f9568e6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java
index ea3057b3f54f..6be287006f42 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java
index 20558aa92f48..84c186bd24e6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java
index d7b1d2989c0e..b093234b2bc5 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java
index d138dc979b1d..0d2037b4ab27 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java
index 36153f2b7037..ee31973c308a 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java
index bf4fbc4eeefb..0571fefe2a43 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java
index d3c1b36bd3a6..f619dabdd50a 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java
index 90e69a168391..fc443fa362bc 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java
index 96bc1049fad4..bf3d58b68692 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java
index 2679494eb6de..1f4bc31f24c6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java
index 170dce76d9c8..2085552e91dd 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java
index 11d12db39397..d9c7d7b7695e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java
index bd2a6008e239..acd2533a38e4 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java
index 99a09cda367f..de9944a8c274 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java
index db83606bdb54..a8639292cbb3 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java
index 4a8f92461301..4999b9bf6850 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java
index 4e4a9ee980fd..ee80a6f72c93 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java
index 3e7de1d08a0e..ec29f7a33b39 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java
index 67d53b34c4d6..ee6a669f2f83 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java
index 470a1ce0fb71..1702b84e703b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java
index 8a982c23b1cb..514ddb9c4b11 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java
index 2c17a698b5e7..fbcee6906bd7 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java
index 099b1f4f6d82..2c5658810b05 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java
index 7f6b4b8b637e..8fce69e62033 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java
index 8592d30f58f0..ef530607bb56 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java
index 539bd2aa8e8c..64c08983a063 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java
index a36a7b6d39a2..939100c47c05 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java
index 90d2a70691ad..728b1995ff52 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java
index 4e5fcf32edd2..bf5ef99ff456 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java
index fd0abf85abfc..d15705e9106d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java
index 9272b1164d07..222a60da3550 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java
index a896d0a8e3e7..7436476b5329 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java
index 671b0a3c7c83..cca97f42e4b7 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java
index 1eb3f9277d56..170ee7313891 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java
index f23d5e269154..184f796ad61c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java
index 161379851ce6..7e75c44089ff 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java
index 14f1c002812f..39c386b645d8 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java
index 8327caf0a124..04ab5310655c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java
index 6c211fb5371b..b71351fca81c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java
index d02cd735e753..e3955c051890 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java
index 0777586dadd9..adf05a6befb1 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java
index 24a949f7b73c..4d657d9a3511 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java
index 4b94bbe5d86d..dc6417416917 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java
index 1784c0557442..25d5631308ef 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java
index f85d3eeae9aa..de2d5489dbcc 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java
index 81f6779e9820..36544c6f8f50 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java
index 9436fadfde72..fb36d0cb495f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java
index 9ebc45848003..4194b12a4a6e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java
index ea159a123d21..355c6e823803 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java
index a42ec7e93c05..401079d0bece 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java
index 6f1007e553cf..322dcbf7453e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java
index 6a738181104b..c98281416012 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java
index e9a365bd72a4..0b1cb32528ff 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java
index fc9191cc2b77..473707201782 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java
index 5919a1dc32c9..204cd70b2f9e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java
index 313e5806cbf7..b3ffed7de91a 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java
index 9c8b3ae1b939..d0ab8de4502d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java
index ea618cc405be..b378b684114e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java
index df6f470450ea..c7c66fe20513 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java
index 63fd7406cee4..98d6bd71c610 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java
index a96031e673cf..206358f21c1d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java
index 3bc25fbbf524..0532e73c9d66 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java
index 7ffdf11d0fa6..f192d7153fce 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java
index cc7f3be26319..0a8909c6c7b5 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java
index 8d54c00f0fe1..bfcb0f410256 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java
index 22e92dd06330..c6b0509d619b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java
index 08ddc8b0227c..45a01eda2fd5 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java
index 429e090642e9..30472811d5d6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java
index d5b31f66cab5..6f1f1a016039 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java
index 8667aaa961a4..c4d279f37a4c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java
index aa202469eb93..c4f600593067 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java
index 9e0210fbd72c..a6858c261eb0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java
index d48916886341..a994cbeaf02c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java
index b06d7eff199d..65412ec84aa4 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java
index 84469375c7e2..573b0ff277cc 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java
index 34540a31ad78..fe3c0fc04a84 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java
index c79b5133afd5..f398899880ce 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java
index 028130ddef96..74931205fd3e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java
index 06a5a8ce8ceb..5e7326985c9d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java
index 78eefc89c8fd..9a217d1fd142 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java
index cd1bd4824175..1ce2270ecc58 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java
index 6c0740c7169e..ed84528fe869 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java
index b95f24b0a1e8..aeb96404a223 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java
index b03cf826c7f4..8959a0c3d50c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java
index c98c09274102..400772231d48 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java
index 625cfc77e15b..732315862eb2 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java
index 58319b3f6326..f4119c28b826 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java
index f741542411f5..9b9c2612fe25 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java
index 87f6a7832526..f125384706ca 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java
index 610345f69051..2ad605d83d04 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java
index 519d4fd6179b..5ef3bf00204b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java
index 322cf639e81d..0c4ed66fc6b7 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java
index f8ccbadf682f..db6bd2429e26 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java
index 16f1059d150f..d2b0bf76158f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java
index c7084fe63b06..3cd5ae6533b6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java
index 9d526b8687ca..6ddfc25deca9 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java
index 8372f6ccffbb..375f0bc08027 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java
index 87e47e76984f..7e2492ace1dd 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java
index aa2e1049e302..190118c551e6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java
index ebaa0801e894..484ba1b88183 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java
index d90356a02fa6..80e4e153a41f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java
index 6db995a7fd0b..fa26c59304f9 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java
index ecea19e2036a..16bf2a208870 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java
index ab86284729c2..e1716dede024 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java
index 23a33f54fcb9..dc6f2adfe951 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java
index 270b5adccde1..d1096c629ed8 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java
@@ -18,8 +18,8 @@ package android.libcore.varhandles;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
diff --git a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
index ba15796f47fe..fc3738c7134a 100644
--- a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
+++ b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
@@ -53,6 +53,7 @@ import com.android.adservices.service.adselection.AdSelectionConfigArgumentUtil;
import com.android.adservices.service.adselection.AdWithBidArgumentUtil;
import com.android.adservices.service.adselection.CustomAudienceBiddingSignalsArgumentUtil;
import com.android.adservices.service.adselection.CustomAudienceScoringSignalsArgumentUtil;
+import com.android.adservices.service.common.NoOpRetryStrategyImpl;
import com.android.adservices.service.js.IsolateSettings;
import com.android.adservices.service.js.JSScriptArgument;
import com.android.adservices.service.js.JSScriptArrayArgument;
@@ -411,7 +412,8 @@ public class JSScriptEnginePerfTests {
jsScript,
args,
functionName,
- IsolateSettings.forMaxHeapSizeEnforcementDisabled());
+ IsolateSettings.forMaxHeapSizeEnforcementDisabled(),
+ new NoOpRetryStrategyImpl());
result.addListener(resultLatch::countDown, sExecutorService);
return result;
}
@@ -430,7 +432,8 @@ public class JSScriptEnginePerfTests {
wasmScript,
args,
functionName,
- IsolateSettings.forMaxHeapSizeEnforcementDisabled());
+ IsolateSettings.forMaxHeapSizeEnforcementDisabled(),
+ new NoOpRetryStrategyImpl());
result.addListener(resultLatch::countDown, sExecutorService);
return result;
}
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 558629537253..0104ee14fec4 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -21,6 +21,7 @@ java_library {
libs: [
"app-compat-annotations",
+ "error_prone_annotations",
"framework",
"services.core",
"unsupportedappusage",
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index 3b9b4e70b310..bb0f3cbd5257 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -9,3 +9,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "start_user_before_scheduled_alarms"
+ namespace: "multiuser"
+ description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
+ bug: "314907186"
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e728a2c55765..d0a1b027ec48 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -183,6 +183,9 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.zone.ZoneOffsetTransition;
+import java.time.zone.ZoneRules;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -194,6 +197,7 @@ import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
/**
@@ -234,8 +238,12 @@ public class AlarmManagerService extends SystemService {
private static final long TEMPORARY_QUOTA_DURATION = INTERVAL_DAY;
- // System property read on some device configurations to initialize time properly.
+ // System properties read on some device configurations to initialize time properly and
+ // perform DST transitions at the bootloader level.
private static final String TIMEOFFSET_PROPERTY = "persist.sys.time.offset";
+ private static final String DST_TRANSITION_PROPERTY = "persist.sys.time.dst_transition";
+ private static final String DST_OFFSET_PROPERTY = "persist.sys.time.dst_offset";
+
private final Intent mBackgroundIntent
= new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
@@ -2200,6 +2208,19 @@ public class AlarmManagerService extends SystemService {
final int gmtOffset = newZone.getOffset(mInjector.getCurrentTimeMillis());
SystemProperties.set(TIMEOFFSET_PROPERTY, String.valueOf(gmtOffset));
+
+ final ZoneRules rules = newZone.toZoneId().getRules();
+ final ZoneOffsetTransition transition = rules.nextTransition(Instant.now());
+ if (null != transition) {
+ // Get the offset between the time after the DST transition and before.
+ final long transitionOffset = TimeUnit.SECONDS.toMillis((
+ transition.getOffsetAfter().getTotalSeconds()
+ - transition.getOffsetBefore().getTotalSeconds()));
+ // Time when the next DST transition is programmed.
+ final long nextTransition = TimeUnit.SECONDS.toMillis(transition.toEpochSecond());
+ SystemProperties.set(DST_TRANSITION_PROPERTY, String.valueOf(nextTransition));
+ SystemProperties.set(DST_OFFSET_PROPERTY, String.valueOf(transitionOffset));
+ }
}
// Clear the default time zone in the system server process. This forces the next call
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index e96d07f44b34..ee9400fb8408 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -46,8 +46,11 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotMapping;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -68,6 +71,8 @@ import com.android.server.utils.AlarmQueue;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
import java.util.function.Predicate;
/**
@@ -1620,9 +1625,21 @@ public final class FlexibilityController extends StateController {
private final Object mSatLock = new Object();
private DeviceIdleInternal mDeviceIdleInternal;
+ private TelephonyManager mTelephonyManager;
+
+ private final boolean mHasFeatureTelephonySubscription;
/** Set of all apps that have been deemed special, keyed by user ID. */
private final SparseSetArray<String> mSpecialApps = new SparseSetArray<>();
+ /**
+ * Set of carrier privileged apps, keyed by the logical ID of the SIM their privileged
+ * for.
+ */
+ @GuardedBy("mSatLock")
+ private final SparseSetArray<String> mCarrierPrivilegedApps = new SparseSetArray<>();
+ @GuardedBy("mSatLock")
+ private final SparseArray<LogicalIndexCarrierPrivilegesCallback>
+ mCarrierPrivilegedCallbacks = new SparseArray<>();
@GuardedBy("mSatLock")
private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>();
@@ -1630,6 +1647,10 @@ public final class FlexibilityController extends StateController {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
+ case TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED:
+ updateCarrierPrivilegedCallbackRegistration();
+ break;
+
case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
break;
@@ -1637,6 +1658,11 @@ public final class FlexibilityController extends StateController {
}
};
+ SpecialAppTracker() {
+ mHasFeatureTelephonySubscription = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION);
+ }
+
public boolean isSpecialApp(final int userId, @NonNull String packageName) {
synchronized (mSatLock) {
if (mSpecialApps.contains(UserHandle.USER_ALL, packageName)) {
@@ -1654,6 +1680,12 @@ public final class FlexibilityController extends StateController {
if (mPowerAllowlistedApps.contains(packageName)) {
return true;
}
+ for (int l = mCarrierPrivilegedApps.size() - 1; l >= 0; --l) {
+ if (mCarrierPrivilegedApps.contains(
+ mCarrierPrivilegedApps.keyAt(l), packageName)) {
+ return true;
+ }
+ }
}
return false;
}
@@ -1669,9 +1701,12 @@ public final class FlexibilityController extends StateController {
private void onSystemServicesReady() {
mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
synchronized (mLock) {
if (mFlexibilityEnabled) {
+ mHandler.post(
+ SpecialAppTracker.this::updateCarrierPrivilegedCallbackRegistration);
mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
}
}
@@ -1686,6 +1721,13 @@ public final class FlexibilityController extends StateController {
private void startTracking() {
IntentFilter filter = new IntentFilter(
PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
+
+ if (mHasFeatureTelephonySubscription) {
+ filter.addAction(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED);
+
+ updateCarrierPrivilegedCallbackRegistration();
+ }
+
mContext.registerReceiver(mBroadcastReceiver, filter);
updatePowerAllowlistCache();
@@ -1695,9 +1737,61 @@ public final class FlexibilityController extends StateController {
mContext.unregisterReceiver(mBroadcastReceiver);
synchronized (mSatLock) {
+ mCarrierPrivilegedApps.clear();
mPowerAllowlistedApps.clear();
mSpecialApps.clear();
+
+ for (int i = mCarrierPrivilegedCallbacks.size() - 1; i >= 0; --i) {
+ mTelephonyManager.unregisterCarrierPrivilegesCallback(
+ mCarrierPrivilegedCallbacks.valueAt(i));
+ }
+ mCarrierPrivilegedCallbacks.clear();
+ }
+ }
+
+ private void updateCarrierPrivilegedCallbackRegistration() {
+ if (mTelephonyManager == null) {
+ return;
+ }
+ if (!mHasFeatureTelephonySubscription) {
+ return;
+ }
+
+ Collection<UiccSlotMapping> simSlotMapping = mTelephonyManager.getSimSlotMapping();
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+ synchronized (mSatLock) {
+ final IntArray callbacksToRemove = new IntArray();
+ for (int i = mCarrierPrivilegedCallbacks.size() - 1; i >= 0; --i) {
+ callbacksToRemove.add(mCarrierPrivilegedCallbacks.keyAt(i));
+ }
+ for (UiccSlotMapping mapping : simSlotMapping) {
+ final int logicalIndex = mapping.getLogicalSlotIndex();
+ if (mCarrierPrivilegedCallbacks.contains(logicalIndex)) {
+ // Callback already exists. No need to create a new one or remove it.
+ callbacksToRemove.remove(logicalIndex);
+ continue;
+ }
+ final LogicalIndexCarrierPrivilegesCallback callback =
+ new LogicalIndexCarrierPrivilegesCallback(logicalIndex);
+ mCarrierPrivilegedCallbacks.put(logicalIndex, callback);
+ // Upon registration, the callbacks will be called with the current list of
+ // apps, so there's no need to query the app list synchronously.
+ mTelephonyManager.registerCarrierPrivilegesCallback(logicalIndex,
+ AppSchedulingModuleThread.getExecutor(), callback);
+ }
+
+ for (int i = callbacksToRemove.size() - 1; i >= 0; --i) {
+ final int logicalIndex = callbacksToRemove.get(i);
+ final LogicalIndexCarrierPrivilegesCallback callback =
+ mCarrierPrivilegedCallbacks.get(logicalIndex);
+ mTelephonyManager.unregisterCarrierPrivilegesCallback(callback);
+ mCarrierPrivilegedCallbacks.remove(logicalIndex);
+ changedPkgs.addAll(mCarrierPrivilegedApps.get(logicalIndex));
+ mCarrierPrivilegedApps.remove(logicalIndex);
+ }
}
+
+ updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
}
/**
@@ -1762,18 +1856,65 @@ public final class FlexibilityController extends StateController {
updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
}
+ class LogicalIndexCarrierPrivilegesCallback implements
+ TelephonyManager.CarrierPrivilegesCallback {
+ public final int logicalIndex;
+
+ LogicalIndexCarrierPrivilegesCallback(int logicalIndex) {
+ this.logicalIndex = logicalIndex;
+ }
+
+ @Override
+ public void onCarrierPrivilegesChanged(@NonNull Set<String> privilegedPackageNames,
+ @NonNull Set<Integer> privilegedUids) {
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+ synchronized (mSatLock) {
+ final ArraySet<String> oldPrivilegedSet =
+ mCarrierPrivilegedApps.get(logicalIndex);
+ if (oldPrivilegedSet != null) {
+ changedPkgs.addAll(oldPrivilegedSet);
+ mCarrierPrivilegedApps.remove(logicalIndex);
+ }
+ for (String pkgName : privilegedPackageNames) {
+ mCarrierPrivilegedApps.add(logicalIndex, pkgName);
+ if (!changedPkgs.remove(pkgName)) {
+ // The package wasn't in the previous set of privileged apps. Add it
+ // since its state has changed.
+ changedPkgs.add(pkgName);
+ }
+ }
+ }
+
+ // The carrier privileged list doesn't provide a simple userId correlation,
+ // so for now, use USER_ALL for these packages.
+ // TODO(141645789): use the UID list to narrow down to specific userIds
+ updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
+ }
+ }
+
public void dump(@NonNull IndentingPrintWriter pw) {
pw.println("Special apps:");
pw.increaseIndent();
synchronized (mSatLock) {
for (int u = 0; u < mSpecialApps.size(); ++u) {
+ pw.print("User ");
pw.print(mSpecialApps.keyAt(u));
pw.print(": ");
pw.println(mSpecialApps.valuesAt(u));
}
pw.println();
+ pw.println("Carrier privileged packages:");
+ pw.increaseIndent();
+ for (int i = 0; i < mCarrierPrivilegedApps.size(); ++i) {
+ pw.print(mCarrierPrivilegedApps.keyAt(i));
+ pw.print(": ");
+ pw.println(mCarrierPrivilegedApps.valuesAt(i));
+ }
+ pw.decreaseIndent();
+
+ pw.println();
pw.print("Power allowlisted packages: ");
pw.println(mPowerAllowlistedApps);
}
diff --git a/api/Android.bp b/api/Android.bp
index 8e063667826c..093ee4beab50 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -298,6 +298,28 @@ packages_to_document = [
"org.xmlpull",
]
+// These are libs from framework-internal-utils that are required (i.e. being referenced)
+// from framework-non-updatable-sources. Add more here when there's a need.
+// DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
+// dependencies gets bigger.
+android_non_updatable_stubs_libs = [
+ "android.hardware.cas-V1.2-java",
+ "android.hardware.health-V1.0-java-constants",
+ "android.hardware.thermal-V1.0-java-constants",
+ "android.hardware.thermal-V2.0-java",
+ "android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.usb-V1.0-java-constants",
+ "android.hardware.usb-V1.1-java-constants",
+ "android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.vibrator-V1.3-java",
+ "framework-protos",
+]
+
+java_defaults {
+ name: "android-non-updatable-stubs-libs-defaults",
+ libs: android_non_updatable_stubs_libs,
+}
+
// Defaults for all stubs that include the non-updatable framework. These defaults do not include
// module symbols, so will not compile correctly on their own. Users must add module APIs to the
// classpath (or sources) somehow.
@@ -329,18 +351,7 @@ stubs_defaults {
// from framework-non-updatable-sources. Add more here when there's a need.
// DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
// dependencies gets bigger.
- libs: [
- "android.hardware.cas-V1.2-java",
- "android.hardware.health-V1.0-java-constants",
- "android.hardware.thermal-V1.0-java-constants",
- "android.hardware.thermal-V2.0-java",
- "android.hardware.tv.input-V1.0-java-constants",
- "android.hardware.usb-V1.0-java-constants",
- "android.hardware.usb-V1.1-java-constants",
- "android.hardware.usb.gadget-V1.0-java",
- "android.hardware.vibrator-V1.3-java",
- "framework-protos",
- ],
+ libs: android_non_updatable_stubs_libs,
flags: [
"--error NoSettingsProvider",
"--error UnhiddenSystemApi",
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 1f5b83ca8ae8..c1add03fa31a 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -458,13 +458,21 @@ java_defaults {
libs: ["stub-annotations"],
}
+java_defaults {
+ name: "android-non-updatable_everything_from_text_defaults",
+ defaults: [
+ "android-non-updatable_from_text_defaults",
+ ],
+ stubs_type: "everything",
+}
+
java_api_library {
name: "android-non-updatable.stubs.from-text",
api_surface: "public",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_stubs_current.from-text",
}
@@ -475,7 +483,7 @@ java_api_library {
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_system_stubs_current.from-text",
}
@@ -487,7 +495,7 @@ java_api_library {
"system-api-stubs-docs-non-updatable.api.contribution",
"test-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_stubs_current.from-text",
}
@@ -499,7 +507,7 @@ java_api_library {
"system-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
}
@@ -515,7 +523,7 @@ java_api_library {
"test-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
// This module is only used for hiddenapi, and other modules should not
@@ -836,6 +844,7 @@ java_api_library {
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -852,6 +861,7 @@ java_api_library {
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -870,6 +880,7 @@ java_api_library {
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -888,6 +899,7 @@ java_api_library {
"system-api-stubs-docs-non-updatable.api.contribution",
],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -908,6 +920,7 @@ java_api_library {
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -922,6 +935,7 @@ java_api_library {
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -947,6 +961,7 @@ java_api_library {
"//visibility:private",
],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -964,6 +979,7 @@ java_api_library {
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
////////////////////////////////////////////////////////////////////////
diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
index 6b1c7e83c826..15109d99a6fd 100644
--- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp
+++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
@@ -144,7 +144,7 @@ TEST(FabricatedOverlayTests, SerializeAndDeserialize) {
"com.example.target:string/string1", Res_value::TYPE_STRING, "foobar", "")
.Build();
ASSERT_TRUE(overlay);
- TemporaryFile tf;
+ TempFrroFile tf;
std::ofstream out(tf.path);
ASSERT_TRUE((*overlay).ToBinaryStream(out));
out.close();
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index a384305da43d..c85619c1e4bf 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -274,7 +274,7 @@ TEST(IdmapTests, FabricatedOverlay) {
.Build();
ASSERT_TRUE(frro);
- TemporaryFile tf;
+ TempFrroFile tf;
std::ofstream out(tf.path);
ASSERT_TRUE((*frro).ToBinaryStream(out));
out.close();
@@ -467,9 +467,9 @@ TEST(IdmapTests, CreateIdmapDataInlineResources) {
TEST(IdmapTests, IdmapHeaderIsUpToDate) {
fclose(stderr); // silence expected warnings from libandroidfw
- const std::string target_apk_path = kIdmapRawTargetPath;
- const std::string overlay_apk_path = kIdmapRawOverlayPath;
- const std::string overlay_name = kIdmapRawOverlayName;
+ const std::string target_apk_path {kIdmapRawTargetPath};
+ const std::string overlay_apk_path {kIdmapRawOverlayPath};
+ const std::string overlay_name {kIdmapRawOverlayName};
const PolicyBitmask policies = kIdmapRawDataPolicies;
const uint32_t target_crc = kIdmapRawDataTargetCrc;
const uint32_t overlay_crc = kIdmapRawOverlayCrc;
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index db44c23a41f9..1d2255378018 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -217,7 +217,7 @@ TEST(ResourceMappingTests, FabricatedOverlay) {
.Build();
ASSERT_TRUE(frro);
- TemporaryFile tf;
+ TempFrroFile tf;
std::ofstream out(tf.path);
ASSERT_TRUE((*frro).ToBinaryStream(out));
out.close();
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index cdc0b8fbb87e..bf01c32c4c86 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -17,11 +17,15 @@
#ifndef IDMAP2_TESTS_TESTHELPERS_H_
#define IDMAP2_TESTS_TESTHELPERS_H_
+#include <stdio.h>
#include <string>
+#include <string_view>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "android-base/file.h"
+
namespace android::idmap2 {
const unsigned char kIdmapRawData[] = {
@@ -197,12 +201,23 @@ const unsigned int kIdmapRawDataOffset = 0x54;
const unsigned int kIdmapRawDataTargetCrc = 0x1234;
const unsigned int kIdmapRawOverlayCrc = 0x5678;
const unsigned int kIdmapRawDataPolicies = 0x11;
-inline const std::string kIdmapRawTargetPath = "targetX.apk";
-inline const std::string kIdmapRawOverlayPath = "overlayX.apk";
-inline const std::string kIdmapRawOverlayName = "OverlayName";
+inline const std::string_view kIdmapRawTargetPath = "targetX.apk";
+inline const std::string_view kIdmapRawOverlayPath = "overlayX.apk";
+inline const std::string_view kIdmapRawOverlayName = "OverlayName";
std::string GetTestDataPath();
+class TempFrroFile : public TemporaryFile {
+public:
+ TempFrroFile() {
+ std::string new_path = path;
+ new_path += ".frro";
+ ::rename(path, new_path.c_str());
+ const auto new_len = new_path.copy(path, sizeof(path) - 1);
+ path[new_len] = '\0';
+ }
+};
+
class Idmap2Tests : public testing::Test {
protected:
void SetUp() override {
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index a7560b23c6bd..12b79f4c42f8 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -23,8 +23,6 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.sysprop.InitProperties;
public class PowerCommand extends Svc.Command {
private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0;
@@ -142,12 +140,10 @@ public class PowerCommand extends Svc.Command {
// Check if remote exception is benign during shutdown. Pm can be killed
// before system server during shutdown, so remote exception can be ignored
// if it is already in shutdown flow.
+ // sys.powerctl is no longer set to avoid a possible DOS attack (see
+ // bionic/libc/bionic/system_property_set.cpp) so we have no real way of knowing if a
+ // remote exception is real or simply because pm is killed (b/318323013)
+ // So we simply do not display anything.
private void maybeLogRemoteException(String msg) {
- String powerProp = SystemProperties.get("sys.powerctl");
- // Also check if userspace reboot is ongoing, since in case of userspace reboot value of the
- // sys.powerctl property will be reset.
- if (powerProp.isEmpty() && !InitProperties.userspace_reboot_in_progress().orElse(false)) {
- System.err.println(msg);
- }
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index b4c3f44b4ec4..4c1b27db3209 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5395,7 +5395,7 @@ package android.app {
public final class AutomaticZenRule implements android.os.Parcelable {
ctor @Deprecated public AutomaticZenRule(String, android.content.ComponentName, android.net.Uri, int, boolean);
- ctor public AutomaticZenRule(@NonNull String, @Nullable android.content.ComponentName, @Nullable android.content.ComponentName, @NonNull android.net.Uri, @Nullable android.service.notification.ZenPolicy, int, boolean);
+ ctor @Deprecated public AutomaticZenRule(@NonNull String, @Nullable android.content.ComponentName, @Nullable android.content.ComponentName, @NonNull android.net.Uri, @Nullable android.service.notification.ZenPolicy, int, boolean);
ctor public AutomaticZenRule(android.os.Parcel);
method public int describeContents();
method public android.net.Uri getConditionId();
@@ -6085,7 +6085,7 @@ package android.app {
public class GrammaticalInflectionManager {
method public int getApplicationGrammaticalGender();
- method @FlaggedApi("android.app.system_terms_of_address_enabled") public int getSystemGrammaticalGender();
+ method @FlaggedApi("android.app.system_terms_of_address_enabled") @RequiresPermission("android.permission.READ_SYSTEM_GRAMMATICAL_GENDER") public int getSystemGrammaticalGender();
method public void setRequestedApplicationGrammaticalGender(int);
}
@@ -13119,7 +13119,6 @@ package android.content.pm {
field public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
field public static final String FEATURE_CANT_SAVE_STATE = "android.software.cant_save_state";
field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
- field @FlaggedApi("android.view.inputmethod.concurrent_input_methods") public static final String FEATURE_CONCURRENT_INPUT_METHODS = "android.software.concurrent_input_methods";
field @Deprecated public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
field public static final String FEATURE_CONTROLS = "android.software.controls";
@@ -16362,7 +16361,7 @@ package android.graphics {
ctor @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public Matrix44();
ctor @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public Matrix44(@NonNull android.graphics.Matrix);
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") @NonNull public android.graphics.Matrix44 concat(@NonNull android.graphics.Matrix44);
- method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public float get(int, int);
+ method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public float get(@IntRange(from=0, to=3) int, @IntRange(from=0, to=3) int);
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public void getValues(@NonNull float[]);
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public boolean invert();
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public boolean isIdentity();
@@ -16371,7 +16370,7 @@ package android.graphics {
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public void reset();
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") @NonNull public android.graphics.Matrix44 rotate(float, float, float, float);
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") @NonNull public android.graphics.Matrix44 scale(float, float, float);
- method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public void set(int, int, float);
+ method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public void set(@IntRange(from=0, to=3) int, @IntRange(from=0, to=3) int, float);
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public void setValues(@NonNull float[]);
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") @NonNull public android.graphics.Matrix44 translate(float, float, float);
}
@@ -41154,19 +41153,19 @@ package android.service.notification {
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
method public final void migrateNotificationFilter(int, @Nullable java.util.List<java.lang.String>);
method public android.os.IBinder onBind(android.content.Intent);
- method public void onInterruptionFilterChanged(int);
- method public void onListenerConnected();
- method public void onListenerDisconnected();
- method public void onListenerHintsChanged(int);
- method public void onNotificationChannelGroupModified(String, android.os.UserHandle, android.app.NotificationChannelGroup, int);
- method public void onNotificationChannelModified(String, android.os.UserHandle, android.app.NotificationChannel, int);
- method public void onNotificationPosted(android.service.notification.StatusBarNotification);
- method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
- method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
- method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
- method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
- method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int);
- method public void onSilentStatusBarIconsVisibilityChanged(boolean);
+ method @UiThread public void onInterruptionFilterChanged(int);
+ method @UiThread public void onListenerConnected();
+ method @UiThread public void onListenerDisconnected();
+ method @UiThread public void onListenerHintsChanged(int);
+ method @UiThread public void onNotificationChannelGroupModified(String, android.os.UserHandle, android.app.NotificationChannelGroup, int);
+ method @UiThread public void onNotificationChannelModified(String, android.os.UserHandle, android.app.NotificationChannel, int);
+ method @UiThread public void onNotificationPosted(android.service.notification.StatusBarNotification);
+ method @UiThread public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
+ method @UiThread public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
+ method @UiThread public void onNotificationRemoved(android.service.notification.StatusBarNotification);
+ method @UiThread public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
+ method @UiThread public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int);
+ method @UiThread public void onSilentStatusBarIconsVisibilityChanged(boolean);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
method public static void requestRebind(android.content.ComponentName);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9c1a8e854e92..0ab2588dd87e 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -652,6 +652,7 @@ package android.webkit {
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.webkit.WebViewProviderResponse> CREATOR;
field public static final int STATUS_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4
+ field public static final int STATUS_FAILED_OTHER = 11; // 0xb
field public static final int STATUS_FAILED_WAITING_FOR_RELRO = 3; // 0x3
field public static final int STATUS_SUCCESS = 0; // 0x0
field @Nullable public final android.content.pm.PackageInfo packageInfo;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1718452548b3..adc7ef1990a5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4386,7 +4386,7 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_MODULE = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
- field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
+ field @Deprecated public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
field public static final int PROTECTION_FLAG_ROLE = 67108864; // 0x4000000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
@@ -4821,7 +4821,7 @@ package android.hardware {
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean areAnySensorPrivacyTogglesEnabled(int);
- method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @NonNull @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public java.util.Map<java.lang.String,java.lang.Boolean> getCameraPrivacyAllowlist();
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @NonNull @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public java.util.List<java.lang.String> getCameraPrivacyAllowlist();
method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public int getSensorPrivacyState(int, int);
method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isCameraPrivacyEnabled(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
@@ -4844,6 +4844,12 @@ package android.hardware {
method public boolean isEnabled();
}
+ @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") public static class SensorPrivacyManager.StateTypes {
+ field public static final int DISABLED = 2; // 0x2
+ field public static final int ENABLED = 1; // 0x1
+ field public static final int ENABLED_EXCEPT_ALLOWLISTED_APPS = 3; // 0x3
+ }
+
}
package android.hardware.biometrics {
@@ -10758,7 +10764,7 @@ package android.os {
method public final double readDouble();
method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
- method @NonNull @Nullable public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long);
+ method @NonNull public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long);
method @Nullable public final android.os.NativeHandle readEmbeddedNativeHandle(long, long);
method public final float readFloat();
method public final java.util.ArrayList<java.lang.Float> readFloatVector();
@@ -12885,7 +12891,7 @@ package android.service.notification {
}
public abstract class NotificationListenerService extends android.app.Service {
- method public void onNotificationRemoved(@NonNull android.service.notification.StatusBarNotification, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.service.notification.NotificationStats, int);
+ method @UiThread public void onNotificationRemoved(@NonNull android.service.notification.StatusBarNotification, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.service.notification.NotificationStats, int);
}
public static class NotificationListenerService.Ranking {
@@ -12963,9 +12969,22 @@ package android.service.ondeviceintelligence {
method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer);
method public abstract void onListFeatures(@NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method public final void updateProcessingState(@NonNull android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException>);
field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
}
+ public abstract static class OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException extends java.lang.Exception {
+ ctor public OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException(int);
+ ctor public OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException(int, @NonNull String);
+ method public int getErrorCode();
+ }
+
+ public static class OnDeviceIntelligenceService.OnDeviceUpdateProcessingException extends android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceIntelligenceServiceException {
+ ctor public OnDeviceIntelligenceService.OnDeviceUpdateProcessingException(int);
+ ctor public OnDeviceIntelligenceService.OnDeviceUpdateProcessingException(int, @NonNull String);
+ field public static final int PROCESSING_UPDATE_STATUS_CONNECTION_FAILED = 1; // 0x1
+ }
+
@FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceTrustedInferenceService extends android.app.Service {
ctor public OnDeviceTrustedInferenceService();
method public final void fetchFeatureFileInputStreamMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.io.FileInputStream>>);
@@ -12973,6 +12992,7 @@ package android.service.ondeviceintelligence {
method @NonNull public abstract void onCountTokens(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
method @NonNull public abstract void onProcessRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
method @NonNull public abstract void onProcessRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method public abstract void onUpdateProcessingState(@NonNull android.os.Bundle, @NonNull android.os.OutcomeReceiver<android.os.PersistableBundle,android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException>);
method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException;
method public final void openFileInputAsync(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.io.FileInputStream>) throws java.io.FileNotFoundException;
field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService";
@@ -15316,12 +15336,12 @@ package android.telephony {
method @Deprecated public boolean getDataEnabled(int);
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion(int);
- method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyAssistancePackage();
+ method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyAssistancePackageName();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
- method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @Nullable @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID}) public android.telephony.CellIdentity getLastKnownCellIdentity();
+ method @FlaggedApi("com.android.server.telecom.flags.get_last_known_cell_identity") @Nullable @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID}) public android.telephony.CellIdentity getLastKnownCellIdentity();
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
method public int getMaxNumberOfSimultaneouslyActiveSims();
method public static long getMaxNumberVerificationTimeoutMillis();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index caddf5aea324..53d0c032dbaf 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1366,7 +1366,7 @@ package android.credentials.selection {
}
@FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class IntentFactory {
- method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.os.IBinder, boolean, @NonNull String);
+ method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.content.Context, @NonNull android.os.IBinder, boolean, @NonNull String);
method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.content.Context, @NonNull android.credentials.selection.RequestInfo, @NonNull java.util.ArrayList<android.credentials.selection.ProviderData>, @NonNull java.util.ArrayList<android.credentials.selection.DisabledProviderData>, @NonNull android.os.ResultReceiver);
}
@@ -1519,6 +1519,7 @@ package android.graphics.fonts {
package android.hardware {
public final class SensorPrivacyManager {
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setCameraPrivacyAllowlist(@NonNull java.util.List<java.lang.String>);
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, int, boolean);
method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyState(int, int, int);
}
@@ -2031,41 +2032,6 @@ package android.media {
method public android.media.PlaybackParams setAudioStretchMode(int);
}
- @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection {
- method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int);
- method public int getSoundSource();
- method @Nullable public android.net.Uri getSoundUri();
- method public int getVibrationSource();
- method @Nullable public android.net.Uri getVibrationUri();
- method public static boolean isRingtoneSelectionUri(@Nullable android.net.Uri);
- method @NonNull public android.net.Uri toUri();
- field public static final String DEFAULT_SELECTION_URI_STRING = "content://media/ringtone";
- field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3
- field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1
- field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2
- field public static final int SOUND_SOURCE_OFF = 1; // 0x1
- field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
- field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0
- field public static final int SOUND_SOURCE_URI = 2; // 0x2
- field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4
- field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa
- field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb
- field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1
- field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
- field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0
- field public static final int VIBRATION_SOURCE_URI = 2; // 0x2
- }
-
- @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public static final class RingtoneSelection.Builder {
- ctor public RingtoneSelection.Builder();
- ctor public RingtoneSelection.Builder(@NonNull android.media.RingtoneSelection);
- method @NonNull public android.media.RingtoneSelection build();
- method @NonNull public android.media.RingtoneSelection.Builder setSoundSource(int);
- method @NonNull public android.media.RingtoneSelection.Builder setSoundSource(@NonNull android.net.Uri);
- method @NonNull public android.media.RingtoneSelection.Builder setVibrationSource(int);
- method @NonNull public android.media.RingtoneSelection.Builder setVibrationSource(@NonNull android.net.Uri);
- }
-
public static final class VolumeShaper.Configuration.Builder {
method @NonNull public android.media.VolumeShaper.Configuration.Builder setOptionFlags(int);
}
@@ -3979,6 +3945,7 @@ package android.view.inputmethod {
method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
+ method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest();
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 658ddbf0abfd..685ea63cb432 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -2055,14 +2055,8 @@ UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_DEFAULT:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
-UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT
-UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED
UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
@@ -2073,10 +2067,6 @@ UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_HAPTIC_GENERATOR:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
diff --git a/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java b/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java
index cac1dc4e04a9..f1df33657279 100644
--- a/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java
+++ b/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java
@@ -25,9 +25,11 @@ import android.hardware.usb.UsbDevice;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.Flags;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FunctionalUtils;
import java.io.IOException;
@@ -36,24 +38,46 @@ import java.util.concurrent.Executor;
/**
* Default implementation of {@link BrailleDisplayController}.
+ *
+ * @hide
*/
// BrailleDisplayControllerImpl is not an API, but it implements BrailleDisplayController APIs.
// This @FlaggedApi annotation tells the linter that this method delegates API checks to its
// callers.
@FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID)
-final class BrailleDisplayControllerImpl implements BrailleDisplayController {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class BrailleDisplayControllerImpl implements BrailleDisplayController {
private final AccessibilityService mAccessibilityService;
private final Object mLock;
+ private final boolean mIsHidrawSupported;
private IBrailleDisplayConnection mBrailleDisplayConnection;
private Executor mCallbackExecutor;
private BrailleDisplayCallback mCallback;
+ /**
+ * Read-only property that returns whether HIDRAW access is supported on this device.
+ *
+ * <p>Defaults to true.
+ *
+ * <p>Device manufacturers without HIDRAW kernel support can set this to false in
+ * the device's product makefile.
+ */
+ private static final boolean IS_HIDRAW_SUPPORTED = SystemProperties.getBoolean(
+ "ro.accessibility.support_hidraw", true);
+
BrailleDisplayControllerImpl(AccessibilityService accessibilityService,
Object lock) {
+ this(accessibilityService, lock, IS_HIDRAW_SUPPORTED);
+ }
+
+ @VisibleForTesting
+ public BrailleDisplayControllerImpl(AccessibilityService accessibilityService,
+ Object lock, boolean isHidrawSupported) {
mAccessibilityService = accessibilityService;
mLock = lock;
+ mIsHidrawSupported = isHidrawSupported;
}
@Override
@@ -113,6 +137,11 @@ final class BrailleDisplayControllerImpl implements BrailleDisplayController {
createConnection,
@NonNull Executor callbackExecutor, @NonNull BrailleDisplayCallback callback) {
BrailleDisplayController.checkApiFlagIsEnabled();
+ if (!mIsHidrawSupported) {
+ callbackExecutor.execute(() -> callback.onConnectionFailed(
+ BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS));
+ return;
+ }
if (isConnected()) {
throw new IllegalStateException(
"This service already has a connected Braille display");
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 57c67be7e625..63cafdc6c855 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9601,9 +9601,9 @@ public class Activity extends ContextThemeWrapper
* Specifies whether the activities below this one in the task can also start other activities
* or finish the task.
* <p>
- * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps
- * are blocked from starting new activities or finishing their task unless the top activity of
- * such task belong to the same UID for security reasons.
+ * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, apps
+ * may be blocked from starting new activities or finishing their task unless the top activity
+ * of such task belong to the same UID for security reasons.
* <p>
* Setting this flag to {@code true} will allow the launching app to ignore the restriction if
* this activity is on top. Apps matching the UID of this activity are always exempt.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 41151c0dc647..1d39186de183 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -40,6 +40,7 @@ import static android.window.ConfigurationHelper.shouldUpdateResources;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL;
import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
+import static com.android.window.flags.Flags.activityWindowInfoFlag;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -63,6 +64,7 @@ import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ActivityResultItem;
import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PendingTransactionActions;
@@ -606,6 +608,8 @@ public final class ActivityThread extends ClientTransactionHandler
Configuration overrideConfig;
@NonNull
private ActivityWindowInfo mActivityWindowInfo;
+ @Nullable
+ private ActivityWindowInfo mLastReportedActivityWindowInfo;
// Used for consolidating configs before sending on to Activity.
private final Configuration tmpConfig = new Configuration();
@@ -4180,6 +4184,9 @@ public final class ActivityThread extends ClientTransactionHandler
pendingActions.setRestoreInstanceState(true);
pendingActions.setCallOnPostCreate(true);
}
+
+ // Trigger ActivityWindowInfo callback if first launch or change from relaunch.
+ handleActivityWindowInfoChanged(r);
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED,
@@ -4558,7 +4565,7 @@ public final class ActivityThread extends ClientTransactionHandler
private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
- r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
+ r.activity.isFinishing(), /* userLeaving */ true,
/* dontReport */ false, /* autoEnteringPip */ false);
transaction.addTransactionItem(pauseActivityItem);
executeTransaction(transaction);
@@ -5432,13 +5439,12 @@ public final class ActivityThread extends ClientTransactionHandler
@Override
public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,
- int configChanges, boolean autoEnteringPip, PendingTransactionActions pendingActions,
+ boolean autoEnteringPip, PendingTransactionActions pendingActions,
String reason) {
if (userLeaving) {
performUserLeavingActivity(r);
}
- r.activity.mConfigChangeFlags |= configChanges;
if (autoEnteringPip) {
// Set mIsInPictureInPictureMode earlier in case of auto-enter-pip, see also
// {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}.
@@ -5687,9 +5693,8 @@ public final class ActivityThread extends ClientTransactionHandler
}
@Override
- public void handleStopActivity(ActivityClientRecord r, int configChanges,
+ public void handleStopActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
- r.activity.mConfigChangeFlags |= configChanges;
final StopInfo stopInfo = new StopInfo();
performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
@@ -5859,11 +5864,10 @@ public final class ActivityThread extends ClientTransactionHandler
/** Core implementation of activity destroy call. */
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
- int configChanges, boolean getNonConfigInstance, String reason) {
+ boolean getNonConfigInstance, String reason) {
Class<? extends Activity> activityClass;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
activityClass = r.activity.getClass();
- r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
@@ -5928,9 +5932,9 @@ public final class ActivityThread extends ClientTransactionHandler
}
@Override
- public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
+ public void handleDestroyActivity(ActivityClientRecord r, boolean finishing,
boolean getNonConfigInstance, String reason) {
- performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
+ performDestroyActivity(r, finishing, getNonConfigInstance, reason);
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
@@ -6130,7 +6134,7 @@ public final class ActivityThread extends ClientTransactionHandler
r.activity.mChangingConfigurations = true;
- handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
+ handleRelaunchActivityInner(r, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, tmp.mActivityWindowInfo,
"handleRelaunchActivity");
}
@@ -6199,7 +6203,7 @@ public final class ActivityThread extends ClientTransactionHandler
executeTransaction(transaction);
}
- private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, int configChanges,
+ private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r,
@Nullable List<ResultInfo> pendingResults,
@Nullable List<ReferrerIntent> pendingIntents,
@NonNull PendingTransactionActions pendingActions, boolean startsNotResumed,
@@ -6215,7 +6219,7 @@ public final class ActivityThread extends ClientTransactionHandler
callActivityOnStop(r, true /* saveState */, reason);
}
- handleDestroyActivity(r, false, configChanges, true, reason);
+ handleDestroyActivity(r, false /* finishing */, true /* getNonConfigInstance */, reason);
r.activity = null;
r.window = null;
@@ -6740,7 +6744,7 @@ public final class ActivityThread extends ClientTransactionHandler
// Perform updates.
r.overrideConfig = overrideConfig;
r.mActivityWindowInfo = activityWindowInfo;
- // TODO(b/287582673): notify on ActivityWindowInfo change
+
final ViewRootImpl viewRoot = r.activity.mDecor != null
? r.activity.mDecor.getViewRootImpl() : null;
@@ -6763,6 +6767,22 @@ public final class ActivityThread extends ClientTransactionHandler
viewRoot.updateConfiguration(displayId);
}
mSomeActivitiesChanged = true;
+
+ // Trigger ActivityWindowInfo callback if changed.
+ handleActivityWindowInfoChanged(r);
+ }
+
+ private void handleActivityWindowInfoChanged(@NonNull ActivityClientRecord r) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ if (r.mActivityWindowInfo == null
+ || r.mActivityWindowInfo.equals(r.mLastReportedActivityWindowInfo)) {
+ return;
+ }
+ r.mLastReportedActivityWindowInfo = r.mActivityWindowInfo;
+ ClientTransactionListenerController.getInstance().onActivityWindowInfoChanged(r.token,
+ r.mActivityWindowInfo);
}
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index f6ec370478a9..5e2397d41424 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -173,8 +173,8 @@ public final class AutomaticZenRule implements Parcelable {
* interrupt the user (e.g. via sound &amp; vibration) while this rule
* is active.
* @param enabled Whether the rule is enabled.
- * @deprecated use {@link #AutomaticZenRule(String, ComponentName, ComponentName, Uri,
- * ZenPolicy, int, boolean)}.
+ *
+ * @deprecated Use {@link AutomaticZenRule.Builder} to construct an {@link AutomaticZenRule}.
*/
@Deprecated
public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
@@ -206,8 +206,10 @@ public final class AutomaticZenRule implements Parcelable {
* while this rule is active. This overrides the global policy while this rule is
* action ({@link Condition#STATE_TRUE}).
* @param enabled Whether the rule is enabled.
+ *
+ * @deprecated Use {@link AutomaticZenRule.Builder} to construct an {@link AutomaticZenRule}.
*/
- // TODO (b/309088420): deprecate this constructor in favor of the builder
+ @Deprecated
public AutomaticZenRule(@NonNull String name, @Nullable ComponentName owner,
@Nullable ComponentName configurationActivity, @NonNull Uri conditionId,
@Nullable ZenPolicy policy, int interruptionFilter, boolean enabled) {
@@ -368,6 +370,9 @@ public final class AutomaticZenRule implements Parcelable {
/**
* Sets the zen policy.
+ *
+ * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
+ * a {@code null} value here means the previous policy is retained.
*/
public void setZenPolicy(@Nullable ZenPolicy zenPolicy) {
this.mZenPolicy = (zenPolicy == null ? null : zenPolicy.copy());
@@ -390,7 +395,12 @@ public final class AutomaticZenRule implements Parcelable {
* Sets the configuration activity - an activity that handles
* {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows the user more information
* about this rule and/or allows them to configure it. This is required to be non-null for rules
- * that are not backed by {@link android.service.notification.ConditionProviderService}.
+ * that are not backed by a {@link android.service.notification.ConditionProviderService}.
+ *
+ * <p>This is exclusive with the {@code owner} supplied in the constructor; rules where a
+ * configuration activity is set will not use the
+ * {@link android.service.notification.ConditionProviderService} supplied there to determine
+ * whether the rule should be active.
*/
public void setConfigurationActivity(@Nullable ComponentName componentName) {
this.configurationActivity = getTrimmedComponentName(componentName);
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index b5b3669c1d80..01153c9e7efc 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -114,11 +114,11 @@ public abstract class ClientTransactionHandler {
/** Destroy the activity. */
public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing,
- int configChanges, boolean getNonConfigInstance, String reason);
+ boolean getNonConfigInstance, String reason);
/** Pause the activity. */
public abstract void handlePauseActivity(@NonNull ActivityClientRecord r, boolean finished,
- boolean userLeaving, int configChanges, boolean autoEnteringPip,
+ boolean userLeaving, boolean autoEnteringPip,
PendingTransactionActions pendingActions, String reason);
/**
@@ -146,14 +146,13 @@ public abstract class ClientTransactionHandler {
/**
* Stop the activity.
* @param r Target activity record.
- * @param configChanges Activity configuration changes.
* @param pendingActions Pending actions to be used on this or later stages of activity
* transaction.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param reason Reason for performing this operation.
*/
- public abstract void handleStopActivity(@NonNull ActivityClientRecord r, int configChanges,
+ public abstract void handleStopActivity(@NonNull ActivityClientRecord r,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
/** Report that activity was stopped to server. */
diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java
index 483a6e11a42a..4ce983f6019b 100644
--- a/core/java/android/app/GrammaticalInflectionManager.java
+++ b/core/java/android/app/GrammaticalInflectionManager.java
@@ -16,8 +16,10 @@
package android.app;
+import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.content.res.Configuration;
@@ -127,6 +129,7 @@ public class GrammaticalInflectionManager {
*
* @see Configuration#getGrammaticalGender
*/
+ @RequiresPermission(Manifest.permission.READ_SYSTEM_GRAMMATICAL_GENDER)
@FlaggedApi(Flags.FLAG_SYSTEM_TERMS_OF_ADDRESS_ENABLED)
@Configuration.GrammaticalGender
public int getSystemGrammaticalGender() {
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 1b19ecdd5931..095cfc5a7303 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -413,7 +413,7 @@ public class LocalActivityManager {
if (localLOGV) Log.v(TAG, r.id + ": destroying");
final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
if (clientRecord != null) {
- mActivityThread.performDestroyActivity(clientRecord, finish, 0 /* configChanges */,
+ mActivityThread.performDestroyActivity(clientRecord, finish,
false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
}
r.activity = null;
@@ -684,7 +684,7 @@ public class LocalActivityManager {
if (localLOGV) Log.v(TAG, r.id + ": no corresponding record");
continue;
}
- mActivityThread.performDestroyActivity(clientRecord, finishing, 0 /* configChanges */,
+ mActivityThread.performDestroyActivity(clientRecord, finishing,
false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy");
}
mActivities.clear();
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index b2be27f70ccc..4a06f7d1a1c3 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -248,7 +248,8 @@ public class LocaleConfig implements Parcelable {
}
/**
- * Returns the default locale if specified, otherwise null
+ * Returns the locale the strings in values/strings.xml (the default strings in the directory
+ * with no locale qualifier) are in if specified, otherwise null
*
* @return The default Locale or null
*/
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index d49a2542eed8..b82a1e3d8dfa 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -270,13 +270,16 @@ public class NotificationManager {
* Integer extra for {@link #ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED} containing the state of
* the {@link AutomaticZenRule}.
*
- * <p>
- * The value will be one of {@link #AUTOMATIC_RULE_STATUS_ENABLED},
- * {@link #AUTOMATIC_RULE_STATUS_DISABLED}, {@link #AUTOMATIC_RULE_STATUS_REMOVED},
- * {@link #AUTOMATIC_RULE_STATUS_UNKNOWN}.
- * </p>
+ * <p>The value will be one of {@link #AUTOMATIC_RULE_STATUS_ENABLED},
+ * {@link #AUTOMATIC_RULE_STATUS_DISABLED}, {@link #AUTOMATIC_RULE_STATUS_REMOVED},
+ * {@link #AUTOMATIC_RULE_STATUS_ACTIVATED}, {@link #AUTOMATIC_RULE_STATUS_DEACTIVATED}, or
+ * {@link #AUTOMATIC_RULE_STATUS_UNKNOWN}.
+ *
+ * <p>Note that the {@link #AUTOMATIC_RULE_STATUS_ACTIVATED} and
+ * {@link #AUTOMATIC_RULE_STATUS_DEACTIVATED} statuses are only sent to packages targeting
+ * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above; apps targeting a lower SDK version
+ * will be sent {@link #AUTOMATIC_RULE_STATUS_UNKNOWN} in their place instead.
*/
- // TODO (b/309101513): Add new status types to javadoc
public static final String EXTRA_AUTOMATIC_ZEN_RULE_STATUS =
"android.app.extra.AUTOMATIC_ZEN_RULE_STATUS";
@@ -370,11 +373,15 @@ public class NotificationManager {
= "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
/**
- * Intent that is broadcast when the state of getNotificationPolicy() changes.
+ * Intent that is broadcast when the state of {@link #getNotificationPolicy()} changes.
*
* <p>This broadcast is only sent to registered receivers and (starting from
* {@link Build.VERSION_CODES#Q}) receivers in packages that have been granted Do Not
* Disturb access (see {@link #isNotificationPolicyAccessGranted()}).
+ *
+ * <p>Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, most calls to
+ * {@link #setNotificationPolicy(Policy)} will update the app's implicit rule policy instead of
+ * the global policy, so this broadcast will be sent much less frequently.
*/
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NOTIFICATION_POLICY_CHANGED
@@ -1378,12 +1385,16 @@ public class NotificationManager {
/**
* Updates the given zen rule.
*
- * <p>
- * Throws a SecurityException if policy access is not granted to this package.
+ * <p>Before {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, updating a rule that is not backed
+ * up by a {@link android.service.notification.ConditionProviderService} will deactivate it if
+ * it was previously active. Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, this
+ * will only happen if the rule's definition is actually changing.
+ *
+ * <p>Throws a SecurityException if policy access is not granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
- * <p>
- * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
+ * <p>Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
+ *
* @param id The id of the rule to update
* @param automaticZenRule the rule to update.
* @return Whether the rule was successfully updated.
@@ -1744,9 +1755,11 @@ public class NotificationManager {
/**
* Gets the current user-specified default notification policy.
*
- * <p>
+ * <p>For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above (with some
+ * exceptions, such as companion device managers) this method will return the policy associated
+ * to their implicit {@link AutomaticZenRule} instead, if it exists. See
+ * {@link #setNotificationPolicy(Policy)}.
*/
- // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public Policy getNotificationPolicy() {
INotificationManager service = getService();
try {
@@ -1757,15 +1770,20 @@ public class NotificationManager {
}
/**
- * Sets the current notification policy.
+ * Sets the current notification policy (which applies when {@link #setInterruptionFilter} is
+ * called with the {@link #INTERRUPTION_FILTER_PRIORITY} value).
*
- * <p>
- * Only available if policy access is granted to this package.
- * See {@link #isNotificationPolicyAccessGranted}.
+ * <p>Apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above (with some
+ * exceptions, such as companion device managers) cannot modify the global notification policy.
+ * Calling this method will instead create or update an {@link AutomaticZenRule} associated to
+ * the app, using a {@link ZenPolicy} corresponding to the {@link Policy} supplied here, and
+ * which will be activated/deactivated by calls to {@link #setInterruptionFilter(int)}.
+ *
+ * <p>Only available if policy access is granted to this package. See
+ * {@link #isNotificationPolicyAccessGranted}.
*
* @param policy The new desired policy.
*/
- // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public void setNotificationPolicy(@NonNull Policy policy) {
setNotificationPolicy(policy, /* fromUser= */ false);
}
@@ -2052,10 +2070,12 @@ public class NotificationManager {
/** Notification senders to prioritize for calls. One of:
* PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */
+ @PrioritySenders
public final int priorityCallSenders;
/** Notification senders to prioritize for messages. One of:
* PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */
+ @PrioritySenders
public final int priorityMessageSenders;
/**
@@ -2063,6 +2083,7 @@ public class NotificationManager {
* {@link #CONVERSATION_SENDERS_NONE}, {@link #CONVERSATION_SENDERS_IMPORTANT},
* {@link #CONVERSATION_SENDERS_ANYONE}.
*/
+ @ConversationSenders
public final int priorityConversationSenders;
/**
@@ -2630,16 +2651,19 @@ public class NotificationManager {
}
/** @hide **/
+ @PrioritySenders
public int allowCallsFrom() {
return priorityCallSenders;
}
/** @hide **/
+ @PrioritySenders
public int allowMessagesFrom() {
return priorityMessageSenders;
}
/** @hide **/
+ @ConversationSenders
public int allowConversationsFrom() {
return priorityConversationSenders;
}
@@ -2780,11 +2804,17 @@ public class NotificationManager {
* The interruption filter defines which notifications are allowed to
* interrupt the user (e.g. via sound &amp; vibration) and is applied
* globally.
- * <p>
- * Only available if policy access is granted to this package. See
+ *
+ * <p>Apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above (with some
+ * exceptions, such as companion device managers) cannot modify the global interruption filter.
+ * Calling this method will instead activate or deactivate an {@link AutomaticZenRule}
+ * associated to the app, using a {@link ZenPolicy} that corresponds to the {@link Policy}
+ * supplied to {@link #setNotificationPolicy(Policy)} (or the global policy when one wasn't
+ * provided).
+ *
+ * <p> Only available if policy access is granted to this package. See
* {@link #isNotificationPolicyAccessGranted}.
*/
- // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
setInterruptionFilter(interruptionFilter, /* fromUser= */ false);
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 625526047212..8b84f062b7b5 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -124,6 +124,32 @@ public class ResourcesManager {
*/
private LocaleConfig mLocaleConfig = new LocaleConfig(LocaleList.getEmptyLocaleList());
+ private final ArrayMap<String, SharedLibraryAssets> mSharedLibAssetsMap =
+ new ArrayMap<>();
+
+ /**
+ * The internal function to register the resources paths of a package (e.g. a shared library).
+ * This will collect the package resources' paths from its ApplicationInfo and add them to all
+ * existing and future contexts while the application is running.
+ */
+ public void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) {
+ SharedLibraryAssets sharedLibAssets = new SharedLibraryAssets(appInfo.sourceDir,
+ appInfo.splitSourceDirs, appInfo.sharedLibraryFiles,
+ appInfo.resourceDirs, appInfo.overlayPaths);
+
+ synchronized (mLock) {
+ if (mSharedLibAssetsMap.containsKey(uniqueId)) {
+ Slog.v(TAG, "Package resources' paths for uniqueId: " + uniqueId
+ + " has already been registered, this is a no-op.");
+ return;
+ }
+ mSharedLibAssetsMap.put(uniqueId, sharedLibAssets);
+ appendLibAssetsLocked(sharedLibAssets.getAllAssetPaths());
+ Slog.v(TAG, "The following resources' paths have been added: "
+ + Arrays.toString(sharedLibAssets.getAllAssetPaths()));
+ }
+ }
+
private static class ApkKey {
public final String path;
public final boolean sharedLib;
@@ -278,6 +304,21 @@ public class ResourcesManager {
public ResourcesManager() {
}
+ /**
+ * Inject a customized ResourcesManager instance for testing, return the old ResourcesManager
+ * instance.
+ */
+ @UnsupportedAppUsage
+ @VisibleForTesting
+ public static ResourcesManager setInstance(ResourcesManager resourcesManager) {
+ synchronized (ResourcesManager.class) {
+ ResourcesManager oldResourceManager = sResourcesManager;
+ sResourcesManager = resourcesManager;
+ return oldResourceManager;
+ }
+
+ }
+
@UnsupportedAppUsage
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
@@ -1480,6 +1521,56 @@ public class ResourcesManager {
}
}
+ private void appendLibAssetsLocked(String[] libAssets) {
+ synchronized (mLock) {
+ // Record which ResourcesImpl need updating
+ // (and what ResourcesKey they should update to).
+ final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+
+ final int implCount = mResourceImpls.size();
+ for (int i = 0; i < implCount; i++) {
+ final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ if (impl == null) {
+ Slog.w(TAG, "Found a ResourcesImpl which is null, skip it and continue to "
+ + "append shared library assets for next ResourcesImpl.");
+ continue;
+ }
+
+ var newDirs = new ArrayList<String>();
+ var dirsSet = new ArraySet<String>();
+ if (key.mLibDirs != null) {
+ final int dirsLength = key.mLibDirs.length;
+ for (int k = 0; k < dirsLength; k++) {
+ newDirs.add(key.mLibDirs[k]);
+ dirsSet.add(key.mLibDirs[k]);
+ }
+ }
+ final int assetsLength = libAssets.length;
+ for (int j = 0; j < assetsLength; j++) {
+ if (dirsSet.add(libAssets[j])) {
+ newDirs.add(libAssets[j]);
+ }
+ }
+ String[] newLibAssets = newDirs.toArray(new String[0]);
+ if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
+ updatedResourceKeys.put(impl, new ResourcesKey(
+ key.mResDir,
+ key.mSplitResDirs,
+ key.mOverlayPaths,
+ newLibAssets,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo,
+ key.mLoaders));
+ }
+ }
+
+ redirectResourcesToNewImplLocked(updatedResourceKeys);
+ }
+ }
+
private void applyNewResourceDirsLocked(@Nullable final String[] oldSourceDirs,
@NonNull final ApplicationInfo appInfo) {
try {
@@ -1689,4 +1780,50 @@ public class ResourcesManager {
}
}
}
+
+ public static class SharedLibraryAssets{
+ private final String[] mAssetPaths;
+
+ SharedLibraryAssets(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles,
+ String[] resourceDirs, String[] overlayPaths) {
+ mAssetPaths = collectAssetPaths(sourceDir, splitSourceDirs, sharedLibraryFiles,
+ resourceDirs, overlayPaths);
+ }
+
+ private @NonNull String[] collectAssetPaths(String sourceDir, String[] splitSourceDirs,
+ String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) {
+ final String[][] inputLists = {
+ splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths
+ };
+
+ final ArraySet<String> assetPathSet = new ArraySet<>();
+ final List<String> assetPathList = new ArrayList<>();
+ if (sourceDir != null) {
+ assetPathSet.add(sourceDir);
+ assetPathList.add(sourceDir);
+ }
+
+ for (int i = 0; i < inputLists.length; i++) {
+ if (inputLists[i] != null) {
+ for (int j = 0; j < inputLists[i].length; j++) {
+ if (assetPathSet.add(inputLists[i][j])) {
+ assetPathList.add(inputLists[i][j]);
+ }
+ }
+ }
+ }
+ return assetPathList.toArray(new String[0]);
+ }
+
+ /**
+ * @return all the asset paths of this collected in this class.
+ */
+ public @NonNull String[] getAllAssetPaths() {
+ return mAssetPaths;
+ }
+ }
+
+ public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() {
+ return new ArrayMap<>(mSharedLibAssetsMap);
+ }
}
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index d81eb20512a2..51f313755e59 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -19,12 +19,12 @@ package android.app.admin;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_ACCOUNT_TYPE;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -54,7 +54,7 @@ public final class AccountTypePolicyKey extends PolicyKey {
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
}
mAccountType = Objects.requireNonNull((accountType));
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index cc5e75fac0ea..cb5e9861141d 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -32,8 +31,8 @@ public final class BundlePolicyValue extends PolicyValue<Bundle> {
public BundlePolicyValue(Bundle value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
- PolicySizeVerifier.enforceMaxParcelableFieldsLength(value);
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
}
}
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index 4d36195613ad..a957dbf132bb 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.Parcel;
@@ -32,7 +31,7 @@ public final class ComponentNamePolicyValue extends PolicyValue<ComponentName> {
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxComponentNameLength(value);
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 083705bca09e..b25ebf69d14c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -14071,7 +14071,7 @@ public class DevicePolicyManager {
public void setAuditLogEnabled(boolean enabled) {
throwIfParentInstance("setAuditLogEnabled");
try {
- mService.setAuditLogEnabled(mContext.getPackageName(), true);
+ mService.setAuditLogEnabled(mContext.getPackageName(), enabled);
} catch (RemoteException re) {
re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IntentFilterPolicyKey.java b/core/java/android/app/admin/IntentFilterPolicyKey.java
index de7ff9f0ad0f..7526a7b2c934 100644
--- a/core/java/android/app/admin/IntentFilterPolicyKey.java
+++ b/core/java/android/app/admin/IntentFilterPolicyKey.java
@@ -19,7 +19,6 @@ package android.app.admin;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_INTENT_FILTER;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,9 +59,6 @@ public final class IntentFilterPolicyKey extends PolicyKey {
@TestApi
public IntentFilterPolicyKey(@NonNull String identifier, @NonNull IntentFilter filter) {
super(identifier);
- if (devicePolicySizeTrackingEnabled()) {
- PolicySizeVerifier.enforceMaxParcelableFieldsLength(filter);
- }
mFilter = Objects.requireNonNull(filter);
}
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index 9d6ce243a19b..a36ea0508a95 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -16,11 +16,10 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -136,7 +135,7 @@ public final class LockTaskPolicy extends PolicyValue<LockTaskPolicy> {
}
private void setPackagesInternal(Set<String> packages) {
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String p : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(p);
}
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 2241fddb7320..389585f036db 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -20,12 +20,12 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_PACKAGE_NAME;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_PERMISSION_NAME;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -59,7 +59,7 @@ public final class PackagePermissionPolicyKey extends PolicyKey {
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
}
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 2ea17a18f6a6..68dc797f6513 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -19,12 +19,12 @@ package android.app.admin;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_PACKAGE_NAME;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -55,7 +55,7 @@ public final class PackagePolicyKey extends PolicyKey {
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
}
mPackageName = Objects.requireNonNull((packageName));
diff --git a/core/java/android/app/admin/PolicySizeVerifier.java b/core/java/android/app/admin/PolicySizeVerifier.java
index 792ebc6ad297..7f8e50ec4420 100644
--- a/core/java/android/app/admin/PolicySizeVerifier.java
+++ b/core/java/android/app/admin/PolicySizeVerifier.java
@@ -17,12 +17,12 @@
package android.app.admin;
import android.content.ComponentName;
+import android.os.Bundle;
import android.os.Parcelable;
import android.os.PersistableBundle;
import com.android.internal.util.Preconditions;
-import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -71,44 +71,51 @@ public class PolicySizeVerifier {
for (String key : current.keySet()) {
enforceMaxStringLength(key, "key in " + argName);
Object value = current.get(key);
- if (value instanceof String) {
- enforceMaxStringLength((String) value, "string value in " + argName);
- } else if (value instanceof String[]) {
- for (String str : (String[]) value) {
+ if (value instanceof String str) {
+ enforceMaxStringLength(str, "string value in " + argName);
+ } else if (value instanceof String[] strArray) {
+ for (String str : strArray) {
enforceMaxStringLength(str, "string value in " + argName);
}
- } else if (value instanceof PersistableBundle) {
- queue.add((PersistableBundle) value);
+ } else if (value instanceof PersistableBundle persistableBundle) {
+ queue.add(persistableBundle);
}
}
}
}
/**
- * Throw if Parcelable contains any string that's too long to be serialized.
+ * Throw if bundle contains any string that's too long to be serialized. This follows the
+ * serialization logic in BundlePolicySerializer#writeBundle.
*/
- public static void enforceMaxParcelableFieldsLength(Parcelable parcelable) {
- // TODO(b/326662716) rework to protect against infinite recursion.
- if (true) {
- return;
- }
- Class<?> clazz = parcelable.getClass();
-
- Field[] fields = clazz.getDeclaredFields();
- for (Field field : fields) {
- field.setAccessible(true);
- try {
- Object value = field.get(parcelable);
- if (value instanceof String) {
- String stringValue = (String) value;
- enforceMaxStringLength(stringValue, field.getName());
+ public static void enforceMaxBundleFieldsLength(Bundle bundle) {
+ Queue<Bundle> queue = new ArrayDeque<>();
+ queue.add(bundle);
+ while (!queue.isEmpty()) {
+ Bundle current = queue.remove();
+ for (String key : current.keySet()) {
+ enforceMaxStringLength(key, "key in Bundle");
+ Object value = current.get(key);
+ if (value instanceof String str) {
+ enforceMaxStringLength(str, "string value in Bundle with "
+ + "key" + key);
+ } else if (value instanceof String[] strArray) {
+ for (String str : strArray) {
+ enforceMaxStringLength(str, "string value in Bundle with"
+ + " key" + key);
+ }
+ } else if (value instanceof Bundle b) {
+ queue.add(b);
}
-
- if (value instanceof Parcelable) {
- enforceMaxParcelableFieldsLength((Parcelable) value);
+ else if (value instanceof Parcelable[] parcelableArray) {
+ for (Parcelable parcelable : parcelableArray) {
+ if (!(parcelable instanceof Bundle)) {
+ throw new IllegalArgumentException("bundle-array can only hold "
+ + "Bundles");
+ }
+ queue.add((Bundle) parcelable);
+ }
}
- } catch (IllegalAccessException e) {
- e.printStackTrace();
}
}
}
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index f4d4adcfcedb..8995c0f20de8 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.Objects;
@@ -31,7 +30,7 @@ public final class StringPolicyValue extends PolicyValue<String> {
public StringPolicyValue(@NonNull String value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
}
}
diff --git a/core/java/android/app/admin/StringSetPolicyValue.java b/core/java/android/app/admin/StringSetPolicyValue.java
index 82fe761a414f..f37dfee0f9dc 100644
--- a/core/java/android/app/admin/StringSetPolicyValue.java
+++ b/core/java/android/app/admin/StringSetPolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.HashSet;
@@ -33,7 +32,7 @@ public final class StringSetPolicyValue extends PolicyValue<Set<String>> {
public StringSetPolicyValue(@NonNull Set<String> value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String str : value) {
PolicySizeVerifier.enforceMaxStringLength(str, "policyValue");
}
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index d69a5f08ce2e..ee90ccd9417f 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -17,11 +17,11 @@
package android.app.admin;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -45,7 +45,7 @@ public final class UserRestrictionPolicyKey extends PolicyKey {
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
}
mRestriction = Objects.requireNonNull(restriction);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 10954aba955c..19270199696e 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -10,7 +10,14 @@ flag {
flag {
name: "device_policy_size_tracking_enabled"
namespace: "enterprise"
- description: "Add feature to track the total policy size and have a max threshold."
+ description: "Add feature to track the total policy size and have a max threshold - public API changes"
+ bug: "281543351"
+}
+
+flag {
+ name: "device_policy_size_tracking_internal_enabled"
+ namespace: "enterprise"
+ description: "Add feature to track the total policy size and have a max threshold - internal changes"
bug: "281543351"
}
@@ -149,3 +156,10 @@ flag {
description: "Allow to query whether MTE is enabled or not to check for compliance for enterprise policy"
bug: "322777918"
}
+
+flag {
+ name: "esim_management_ux_enabled"
+ namespace: "enterprise"
+ description: "Enable UX changes for esim management"
+ bug: "295301164"
+}
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index 812bf8e6be6a..c66478f6cf84 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -145,6 +145,25 @@ public class BackupManagerMonitor {
*/
public static final String EXTRA_LOG_OPERATION_TYPE = "android.app.backup.extra.OPERATION_TYPE";
+ /**
+ * List of system components that do not support restore in a V-> U OS downgrade, even if
+ * restoreAnyVersion is set to true.
+ * Read from Settings.Secure.V_TO_U_RESTORE_DENYLIST
+ *
+ * @hide
+ */
+ public static final String EXTRA_LOG_V_TO_U_DENYLIST = "android.app.backup.extra.V_TO_U_DENYLIST";
+
+ /**
+ * List of system components that support restore in a V-> U OS downgrade, even if
+ * restoreAnyVersion is set to false.
+ * Read from Settings.Secure.V_TO_U_RESTORE_ALLOWLIST
+ *
+ * @hide
+ */
+ public static final String EXTRA_LOG_V_TO_U_ALLOWLIST =
+ "android.app.backup.extra.V_TO_U_ALLOWLIST";
+
// TODO complete this list with all log messages. And document properly.
public static final int LOG_EVENT_ID_FULL_BACKUP_CANCEL = 4;
public static final int LOG_EVENT_ID_ILLEGAL_KEY = 5;
@@ -241,6 +260,15 @@ public class BackupManagerMonitor {
/** Agent error during {@link PerformUnifiedRestoreTask#restoreFinished()}
@hide */
public static final int LOG_EVENT_ID_AGENT_FAILURE = 69;
+ /** V to U restore attempt, pkg is eligible
+ @hide */
+ public static final int LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE = 70;
+ /** V to U restore attempt, pkg is not eligible
+ @hide */
+ public static final int LOG_EVENT_ID_V_TO_U_RESTORE_PKG_NOT_ELIGIBLE = 71;
+ /** V to U restore attempt, allowlist and denlist are set
+ @hide */
+ public static final int LOG_EVENT_ID_V_TO_U_RESTORE_SET_LIST = 72;
/**
* This method will be called each time something important happens on BackupManager.
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 48ea846e8d50..631772556879 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -40,6 +40,7 @@ import java.util.Objects;
public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
private Configuration mConfiguration;
+ private ActivityWindowInfo mActivityWindowInfo;
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -55,8 +56,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
// TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
client.handleActivityConfigurationChanged(r, mConfiguration, INVALID_DISPLAY,
- // TODO(b/287582673): add ActivityWindowInfo
- new ActivityWindowInfo());
+ mActivityWindowInfo);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -73,7 +73,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
/** Obtain an instance initialized with provided params. */
@NonNull
public static ActivityConfigurationChangeItem obtain(@NonNull IBinder activityToken,
- @NonNull Configuration config) {
+ @NonNull Configuration config, @NonNull ActivityWindowInfo activityWindowInfo) {
ActivityConfigurationChangeItem instance =
ObjectPool.obtain(ActivityConfigurationChangeItem.class);
if (instance == null) {
@@ -81,6 +81,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
}
instance.setActivityToken(activityToken);
instance.mConfiguration = new Configuration(config);
+ instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
return instance;
}
@@ -89,6 +90,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
public void recycle() {
super.recycle();
mConfiguration = null;
+ mActivityWindowInfo = null;
ObjectPool.recycle(this);
}
@@ -100,12 +102,14 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeTypedObject(mConfiguration, flags);
+ dest.writeTypedObject(mActivityWindowInfo, flags);
}
/** Read from Parcel. */
private ActivityConfigurationChangeItem(@NonNull Parcel in) {
super(in);
mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
}
public static final @NonNull Creator<ActivityConfigurationChangeItem> CREATOR =
@@ -128,7 +132,8 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
return false;
}
final ActivityConfigurationChangeItem other = (ActivityConfigurationChangeItem) o;
- return Objects.equals(mConfiguration, other.mConfiguration);
+ return Objects.equals(mConfiguration, other.mConfiguration)
+ && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo);
}
@Override
@@ -136,12 +141,14 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
int result = 17;
result = 31 * result + super.hashCode();
result = 31 * result + Objects.hashCode(mConfiguration);
+ result = 31 * result + Objects.hashCode(mActivityWindowInfo);
return result;
}
@Override
public String toString() {
return "ActivityConfigurationChange{" + super.toString()
- + ",config=" + mConfiguration + "}";
+ + ",config=" + mConfiguration
+ + ",activityWindowInfo=" + mActivityWindowInfo + "}";
}
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 79696e047904..48081bb04863 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
import android.app.IApplicationThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -54,10 +55,12 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
@Nullable
private List<ClientTransactionItem> mTransactionItems;
- /** A list of individual callbacks to a client. */
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- @UnsupportedAppUsage
+ /** @deprecated use {@link #getTransactionItems} instead. */
@Nullable
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code #getTransactionItems()}")
+ @Deprecated
private List<ClientTransactionItem> mActivityCallbacks;
/**
@@ -126,42 +129,42 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
setActivityTokenIfNotSet(activityCallback);
}
- /**
- * Gets the list of callbacks.
- * @deprecated use {@link #getTransactionItems()} instead.
- */
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- @Nullable
+ /** @deprecated use {@link #getTransactionItems()} instead. */
@VisibleForTesting
- @UnsupportedAppUsage
+ @Nullable
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code #getTransactionItems()}")
@Deprecated
public List<ClientTransactionItem> getCallbacks() {
return mActivityCallbacks;
}
/**
- * @deprecated a transaction can contain {@link ClientTransactionItem} of different activities,
+ * A transaction can contain {@link ClientTransactionItem} of different activities,
* this must not be used. For any unsupported app usages, please be aware that this is set to
* the activity of the first item in {@link #getTransactionItems()}.
+ *
+ * @deprecated use {@link ClientTransactionItem#getActivityToken()} instead.
*/
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@VisibleForTesting
@Nullable
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code android.app.servertransaction"
+ + ".ClientTransactionItem#getActivityToken()}")
@Deprecated
public IBinder getActivityToken() {
return mActivityToken;
}
- /**
- * Gets the target state lifecycle request.
- * @deprecated use {@link #getTransactionItems()} instead.
- */
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+ /** @deprecated use {@link #getTransactionItems()} instead. */
@VisibleForTesting(visibility = PACKAGE)
- @UnsupportedAppUsage
- @Deprecated
@Nullable
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code #getTransactionItems()}")
+ @Deprecated
public ActivityLifecycleItem getLifecycleStateRequest() {
return mLifecycleStateRequest;
}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 1a8136e06c28..7383d07c82e9 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -16,16 +16,24 @@
package android.app.servertransaction;
+import static com.android.window.flags.Flags.activityWindowInfoFlag;
import static com.android.window.flags.Flags.bundleClientTransactionFlag;
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.app.Activity;
import android.app.ActivityThread;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.window.ActivityWindowInfo;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.function.BiConsumer;
+
/**
* Singleton controller to manage listeners to individual {@link ClientTransaction}.
*
@@ -35,8 +43,14 @@ public class ClientTransactionListenerController {
private static ClientTransactionListenerController sController;
+ private final Object mLock = new Object();
private final DisplayManagerGlobal mDisplayManager;
+ /** Listeners registered via {@link #registerActivityWindowInfoChangedListener(BiConsumer)}. */
+ @GuardedBy("mLock")
+ private final ArraySet<BiConsumer<IBinder, ActivityWindowInfo>>
+ mActivityWindowInfoChangedListeners = new ArraySet<>();
+
/** Gets the singleton controller. */
@NonNull
public static ClientTransactionListenerController getInstance() {
@@ -62,6 +76,57 @@ public class ClientTransactionListenerController {
}
/**
+ * Registers to listen on activity {@link ActivityWindowInfo} change.
+ * The listener will be invoked with two parameters: {@link Activity#getActivityToken()} and
+ * {@link ActivityWindowInfo}.
+ */
+ public void registerActivityWindowInfoChangedListener(
+ @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ mActivityWindowInfoChangedListeners.add(listener);
+ }
+ }
+
+ /**
+ * Unregisters the listener that was previously registered via
+ * {@link #registerActivityWindowInfoChangedListener(BiConsumer)}
+ */
+ public void unregisterActivityWindowInfoChangedListener(
+ @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ mActivityWindowInfoChangedListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Called when receives a {@link ClientTransaction} that is updating an activity's
+ * {@link ActivityWindowInfo}.
+ */
+ public void onActivityWindowInfoChanged(@NonNull IBinder activityToken,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ final Object[] activityWindowInfoChangedListeners;
+ synchronized (mLock) {
+ if (mActivityWindowInfoChangedListeners.isEmpty()) {
+ return;
+ }
+ activityWindowInfoChangedListeners = mActivityWindowInfoChangedListeners.toArray();
+ }
+ for (Object activityWindowInfoChangedListener : activityWindowInfoChangedListeners) {
+ ((BiConsumer<IBinder, ActivityWindowInfo>) activityWindowInfoChangedListener)
+ .accept(activityToken, activityWindowInfo);
+ }
+ }
+
+ /**
* Called when receives a {@link ClientTransaction} that is updating display-related
* window configuration.
*/
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index f9cf075d6062..b0213d7356df 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -33,7 +33,6 @@ import android.os.Trace;
public class DestroyActivityItem extends ActivityLifecycleItem {
private boolean mFinished;
- private int mConfigChanges;
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -44,7 +43,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
- client.handleDestroyActivity(r, mFinished, mConfigChanges,
+ client.handleDestroyActivity(r, mFinished,
false /* getNonConfigInstance */, "DestroyActivityItem");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -67,15 +66,13 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
/** Obtain an instance initialized with provided params. */
@NonNull
- public static DestroyActivityItem obtain(@NonNull IBinder activityToken, boolean finished,
- int configChanges) {
+ public static DestroyActivityItem obtain(@NonNull IBinder activityToken, boolean finished) {
DestroyActivityItem instance = ObjectPool.obtain(DestroyActivityItem.class);
if (instance == null) {
instance = new DestroyActivityItem();
}
instance.setActivityToken(activityToken);
instance.mFinished = finished;
- instance.mConfigChanges = configChanges;
return instance;
}
@@ -84,7 +81,6 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
public void recycle() {
super.recycle();
mFinished = false;
- mConfigChanges = 0;
ObjectPool.recycle(this);
}
@@ -95,14 +91,12 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
- dest.writeInt(mConfigChanges);
}
/** Read from Parcel. */
private DestroyActivityItem(@NonNull Parcel in) {
super(in);
mFinished = in.readBoolean();
- mConfigChanges = in.readInt();
}
public static final @NonNull Creator<DestroyActivityItem> CREATOR = new Creator<>() {
@@ -124,7 +118,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
return false;
}
final DestroyActivityItem other = (DestroyActivityItem) o;
- return mFinished == other.mFinished && mConfigChanges == other.mConfigChanges;
+ return mFinished == other.mFinished;
}
@Override
@@ -132,14 +126,12 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
int result = 17;
result = 31 * result + super.hashCode();
result = 31 * result + (mFinished ? 1 : 0);
- result = 31 * result + mConfigChanges;
return result;
}
@Override
public String toString() {
return "DestroyActivityItem{" + super.toString()
- + ",finished=" + mFinished
- + ",mConfigChanges=" + mConfigChanges + "}";
+ + ",finished=" + mFinished + "}";
}
}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 8f1e90b985e6..d230284287b6 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -37,7 +37,6 @@ public class PauseActivityItem extends ActivityLifecycleItem {
private boolean mFinished;
private boolean mUserLeaving;
- private int mConfigChanges;
private boolean mDontReport;
private boolean mAutoEnteringPip;
@@ -45,7 +44,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, mAutoEnteringPip,
+ client.handlePauseActivity(r, mFinished, mUserLeaving, mAutoEnteringPip,
pendingActions, "PAUSE_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -72,7 +71,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
/** Obtain an instance initialized with provided params. */
@NonNull
public static PauseActivityItem obtain(@NonNull IBinder activityToken, boolean finished,
- boolean userLeaving, int configChanges, boolean dontReport, boolean autoEnteringPip) {
+ boolean userLeaving, boolean dontReport, boolean autoEnteringPip) {
PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class);
if (instance == null) {
instance = new PauseActivityItem();
@@ -80,7 +79,6 @@ public class PauseActivityItem extends ActivityLifecycleItem {
instance.setActivityToken(activityToken);
instance.mFinished = finished;
instance.mUserLeaving = userLeaving;
- instance.mConfigChanges = configChanges;
instance.mDontReport = dontReport;
instance.mAutoEnteringPip = autoEnteringPip;
@@ -91,7 +89,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
@NonNull
public static PauseActivityItem obtain(@NonNull IBinder activityToken) {
return obtain(activityToken, false /* finished */, false /* userLeaving */,
- 0 /* configChanges */, true /* dontReport */, false /* autoEnteringPip*/);
+ true /* dontReport */, false /* autoEnteringPip*/);
}
@Override
@@ -99,7 +97,6 @@ public class PauseActivityItem extends ActivityLifecycleItem {
super.recycle();
mFinished = false;
mUserLeaving = false;
- mConfigChanges = 0;
mDontReport = false;
mAutoEnteringPip = false;
ObjectPool.recycle(this);
@@ -113,7 +110,6 @@ public class PauseActivityItem extends ActivityLifecycleItem {
super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
dest.writeBoolean(mUserLeaving);
- dest.writeInt(mConfigChanges);
dest.writeBoolean(mDontReport);
dest.writeBoolean(mAutoEnteringPip);
}
@@ -123,7 +119,6 @@ public class PauseActivityItem extends ActivityLifecycleItem {
super(in);
mFinished = in.readBoolean();
mUserLeaving = in.readBoolean();
- mConfigChanges = in.readInt();
mDontReport = in.readBoolean();
mAutoEnteringPip = in.readBoolean();
}
@@ -148,7 +143,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
}
final PauseActivityItem other = (PauseActivityItem) o;
return mFinished == other.mFinished && mUserLeaving == other.mUserLeaving
- && mConfigChanges == other.mConfigChanges && mDontReport == other.mDontReport
+ && mDontReport == other.mDontReport
&& mAutoEnteringPip == other.mAutoEnteringPip;
}
@@ -158,7 +153,6 @@ public class PauseActivityItem extends ActivityLifecycleItem {
result = 31 * result + super.hashCode();
result = 31 * result + (mFinished ? 1 : 0);
result = 31 * result + (mUserLeaving ? 1 : 0);
- result = 31 * result + mConfigChanges;
result = 31 * result + (mDontReport ? 1 : 0);
result = 31 * result + (mAutoEnteringPip ? 1 : 0);
return result;
@@ -169,7 +163,6 @@ public class PauseActivityItem extends ActivityLifecycleItem {
return "PauseActivityItem{" + super.toString()
+ ",finished=" + mFinished
+ ",userLeaving=" + mUserLeaving
- + ",configChanges=" + mConfigChanges
+ ",dontReport=" + mDontReport
+ ",autoEnteringPip=" + mAutoEnteringPip + "}";
}
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index b8ce52da5a0c..def7b3fd9987 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -19,7 +19,6 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
@@ -34,13 +33,11 @@ public class StopActivityItem extends ActivityLifecycleItem {
private static final String TAG = "StopActivityItem";
- private int mConfigChanges;
-
@Override
public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
- client.handleStopActivity(r, mConfigChanges, pendingActions,
+ client.handleStopActivity(r, pendingActions,
true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -63,16 +60,14 @@ public class StopActivityItem extends ActivityLifecycleItem {
/**
* Obtain an instance initialized with provided params.
* @param activityToken the activity that stops.
- * @param configChanges Configuration pieces that changed.
*/
@NonNull
- public static StopActivityItem obtain(@NonNull IBinder activityToken, int configChanges) {
+ public static StopActivityItem obtain(@NonNull IBinder activityToken) {
StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class);
if (instance == null) {
instance = new StopActivityItem();
}
instance.setActivityToken(activityToken);
- instance.mConfigChanges = configChanges;
return instance;
}
@@ -80,23 +75,14 @@ public class StopActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
super.recycle();
- mConfigChanges = 0;
ObjectPool.recycle(this);
}
// Parcelable implementation
- /** Write to Parcel. */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(mConfigChanges);
- }
-
/** Read from Parcel. */
private StopActivityItem(@NonNull Parcel in) {
super(in);
- mConfigChanges = in.readInt();
}
public static final @NonNull Creator<StopActivityItem> CREATOR = new Creator<>() {
@@ -110,28 +96,7 @@ public class StopActivityItem extends ActivityLifecycleItem {
};
@Override
- public boolean equals(@Nullable Object o) {
- if (this == o) {
- return true;
- }
- if (!super.equals(o)) {
- return false;
- }
- final StopActivityItem other = (StopActivityItem) o;
- return mConfigChanges == other.mConfigChanges;
- }
-
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + super.hashCode();
- result = 31 * result + mConfigChanges;
- return result;
- }
-
- @Override
public String toString() {
- return "StopActivityItem{" + super.toString()
- + ",configChanges=" + mConfigChanges + "}";
+ return "StopActivityItem{" + super.toString() + "}";
}
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index fa73c99be2b8..c83719149821 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -334,18 +334,18 @@ public class TransactionExecutor {
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r, false /* finished */,
- false /* userLeaving */, 0 /* configChanges */,
+ false /* userLeaving */,
false /* autoEnteringPip */, mPendingActions,
"LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
- mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,
+ mTransactionHandler.handleStopActivity(r,
mPendingActions, false /* finalStateRequest */,
"LIFECYCLER_STOP_ACTIVITY");
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r, false /* finishing */,
- 0 /* configChanges */, false /* getNonConfigInstance */,
+ false /* getNonConfigInstance */,
"performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 475c6fb9a48a..710261ab4c97 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -200,7 +200,7 @@ public class TransactionExecutorHelper {
lifecycleItem = PauseActivityItem.obtain(r.token);
break;
case ON_STOP:
- lifecycleItem = StopActivityItem.obtain(r.token, 0 /* configChanges */);
+ lifecycleItem = StopActivityItem.obtain(r.token);
break;
default:
lifecycleItem = ResumeActivityItem.obtain(r.token, false /* isForward */,
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 39f6de75928a..00d534370fa1 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -16,6 +16,9 @@
package android.companion.virtual;
+import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -352,12 +355,20 @@ public class VirtualDeviceInternal {
@Nullable Executor executor,
@Nullable VirtualAudioDevice.AudioConfigurationChangeCallback callback) {
if (mVirtualAudioDevice == null) {
- Context context = mContext;
- if (Flags.deviceAwareRecordAudioPermission()) {
- context = mContext.createDeviceContext(getDeviceId());
+ try {
+ Context context = mContext;
+ if (Flags.deviceAwareRecordAudioPermission()) {
+ // When using a default policy for audio device-aware RECORD_AUDIO permission
+ // should not take effect, thus register policies with the default context.
+ if (mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM) {
+ context = mContext.createDeviceContext(getDeviceId());
+ }
+ }
+ mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display,
+ executor, callback, () -> mVirtualAudioDevice = null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display,
- executor, callback, () -> mVirtualAudioDevice = null);
}
return mVirtualAudioDevice;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fd2af99b6a7e..42dd87a711f3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1064,7 +1064,11 @@ public class Intent implements Parcelable, Cloneable {
}
if (sender != null) {
- intent.putExtra(EXTRA_CHOOSER_RESULT_INTENT_SENDER, sender);
+ if (android.service.chooser.Flags.enableChooserResult()) {
+ intent.putExtra(EXTRA_CHOOSER_RESULT_INTENT_SENDER, sender);
+ } else {
+ intent.putExtra(EXTRA_CHOSEN_COMPONENT_INTENT_SENDER, sender);
+ }
}
// Migrate any clip data and flags from target.
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index cec49c710d92..533fa512dae8 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -67,6 +67,7 @@ interface ILauncherApps {
List<String> getPreInstalledSystemPackages(in UserHandle user);
IntentSender getAppMarketActivityIntent(String callingPackage, String packageName,
in UserHandle user);
+ IntentSender getPrivateSpaceSettingsIntent();
void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
String callingFeatureId, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index e437925693af..6bb9c33afb6a 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -899,6 +899,28 @@ public class LauncherApps {
}
/**
+ * Returns {@link IntentSender} which can be used to start the Private Space Settings Activity.
+ *
+ * <p> Caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the
+ * permissions required.</p>
+ *
+ * @return {@link IntentSender} object which launches the Private Space Settings Activity, if
+ * successful, null otherwise.
+ * @hide
+ */
+ @Nullable
+ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
+ public IntentSender getPrivateSpaceSettingsIntent() {
+ try {
+ return mService.getPrivateSpaceSettingsIntent();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
* returns null.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 240cff34ef36..9f2f74b66eb3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4202,15 +4202,6 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device supports multiple concurrent IME sessions.
- */
- @FlaggedApi("android.view.inputmethod.concurrent_input_methods")
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_CONCURRENT_INPUT_METHODS =
- "android.software.concurrent_input_methods";
-
- /**
- * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports device policy enforcement via device admins.
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index cdda12eebdc4..3f941dad2c3f 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -273,6 +273,10 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
* to the <code>retailDemo</code> value of
* {@link android.R.attr#protectionLevel}.
*
+ * @deprecated This flag has been replaced by the
+ * {@link android.R.string#config_defaultRetailDemo retail demo role} and is a
+ * no-op since {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}.
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 3e9f260566bd..8a3a3ad56a7b 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -112,6 +112,12 @@ public class UserInfo implements Parcelable {
/**
* Indicates that this user is disabled.
*
+ * <p> This is currently used to indicate that a Managed Profile, when created via
+ * DevicePolicyManager, has not yet been provisioned; once the DPC provisions it, a DPM call
+ * will manually set it to enabled.
+ *
+ * <p>Users that are slated for deletion are also generally set to disabled.
+ *
* <p>Note: If an ephemeral user is disabled, it shouldn't be later re-enabled. Ephemeral users
* are disabled as their removal is in progress to indicate that they shouldn't be re-entered.
*/
@@ -398,6 +404,7 @@ public class UserInfo implements Parcelable {
return UserManager.isUserTypePrivateProfile(userType);
}
+ /** See {@link #FLAG_DISABLED}*/
@UnsupportedAppUsage
public boolean isEnabled() {
return (flags & FLAG_DISABLED) != FLAG_DISABLED;
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 5e9d8f0a9b7e..610057bffdbf 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -208,6 +208,14 @@ flag {
}
flag {
+ name: "restrict_nonpreloads_system_shareduids"
+ namespace: "package_manager_service"
+ description: "Feature flag to restrict apps from joining system shared uids"
+ bug: "308573169"
+ is_fixed_read_only: true
+}
+
+flag {
name: "min_target_sdk_24"
namespace: "responsible_apis"
description: "Feature flag to bump min target sdk to 24"
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index d259e9755a41..273e40a21bb2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -471,6 +471,16 @@ public final class AssetManager implements AutoCloseable {
return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
}
+ /**
+ * @hide
+ */
+ public void addSharedLibraryPaths(@NonNull String[] paths) {
+ final int length = paths.length;
+ for (int i = 0; i < length; i++) {
+ addAssetPathInternal(paths[i], false, true);
+ }
+ }
+
private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
Objects.requireNonNull(path, "path");
synchronized (this) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7fba3e890ec6..1f5f88f51d55 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -43,6 +43,7 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.annotation.XmlRes;
import android.app.Application;
+import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -2854,6 +2855,11 @@ public class Resources {
@FlaggedApi(android.content.res.Flags.FLAG_REGISTER_RESOURCE_PATHS)
public static void registerResourcePaths(@NonNull String uniqueId,
@NonNull ApplicationInfo appInfo) {
- throw new UnsupportedOperationException("The implementation has not been done yet.");
+ if (Flags.registerResourcePaths()) {
+ ResourcesManager.getInstance().registerResourcePaths(uniqueId, appInfo);
+ } else {
+ throw new UnsupportedOperationException("Flag " + Flags.FLAG_REGISTER_RESOURCE_PATHS
+ + " is disabled.");
+ }
}
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 079c2c1ab7c9..8d045aaf4d81 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -29,6 +29,7 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.app.LocaleConfig;
import android.app.ResourcesManager;
+import android.app.ResourcesManager.SharedLibraryAssets;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
@@ -47,6 +48,7 @@ import android.os.Build;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.Trace;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -197,6 +199,14 @@ public class ResourcesImpl {
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
mAssets = assets;
+ if (Flags.registerResourcePaths()) {
+ ArrayMap<String, SharedLibraryAssets> sharedLibMap =
+ ResourcesManager.getInstance().getSharedLibAssetsMap();
+ final int size = sharedLibMap.size();
+ for (int i = 0; i < size; i++) {
+ assets.addSharedLibraryPaths(sharedLibMap.valueAt(i).getAllAssetPaths());
+ }
+ }
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 2e63664df7aa..3a9a0f911660 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -23,6 +23,7 @@ import android.annotation.Hide;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -31,6 +32,7 @@ import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -57,6 +59,7 @@ import java.util.concurrent.Executor;
* to authenticate to the app.
*/
@SystemService(Context.CREDENTIAL_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_CREDENTIALS)
public final class CredentialManager {
private static final String TAG = "CredentialManager";
private static final Bundle OPTIONS_SENDER_BAL_OPTIN = ActivityOptions.makeBasic()
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index 4b0fa6d32af7..79fba9b19250 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -80,17 +80,7 @@ public class IntentFactory {
ArrayList<DisabledProviderData> disabledProviderDataList,
@NonNull ResultReceiver resultReceiver) {
Intent intent = new Intent();
- ComponentName componentName =
- ComponentName.unflattenFromString(
- Resources.getSystem()
- .getString(
- com.android.internal.R.string
- .config_credentialManagerDialogComponent));
- ComponentName oemOverrideComponentName = getOemOverrideComponentName(context);
- if (oemOverrideComponentName != null) {
- componentName = oemOverrideComponentName;
- }
- intent.setComponent(componentName);
+ setCredentialSelectorUiComponentName(context, intent);
intent.putParcelableArrayListExtra(
ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList);
intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo);
@@ -100,6 +90,24 @@ public class IntentFactory {
return intent;
}
+ private static void setCredentialSelectorUiComponentName(@NonNull Context context,
+ @NonNull Intent intent) {
+ if (configurableSelectorUiEnabled()) {
+ ComponentName componentName = getOemOverrideComponentName(context);
+ if (componentName == null) {
+ componentName = ComponentName.unflattenFromString(Resources.getSystem().getString(
+ com.android.internal.R.string
+ .config_fallbackCredentialManagerDialogComponent));
+ }
+ intent.setComponent(componentName);
+ } else {
+ ComponentName componentName = ComponentName.unflattenFromString(Resources.getSystem()
+ .getString(com.android.internal.R.string
+ .config_fallbackCredentialManagerDialogComponent));
+ intent.setComponent(componentName);
+ }
+ }
+
/**
* Returns null if there is not an enabled and valid oem override component. It means the
* default platform UI component name should be used instead.
@@ -107,44 +115,39 @@ public class IntentFactory {
@Nullable
private static ComponentName getOemOverrideComponentName(@NonNull Context context) {
ComponentName result = null;
- if (configurableSelectorUiEnabled()) {
- if (Resources.getSystem().getBoolean(
- com.android.internal.R.bool.config_enableOemCredentialManagerDialogComponent)) {
- String oemComponentString =
- Resources.getSystem()
- .getString(
- com.android.internal.R.string
- .config_oemCredentialManagerDialogComponent);
- if (!TextUtils.isEmpty(oemComponentString)) {
- ComponentName oemComponentName = ComponentName.unflattenFromString(
- oemComponentString);
- if (oemComponentName != null) {
- try {
- ActivityInfo info = context.getPackageManager().getActivityInfo(
- oemComponentName,
- PackageManager.ComponentInfoFlags.of(
- PackageManager.MATCH_SYSTEM_ONLY));
- if (info.enabled && info.exported) {
- Slog.i(TAG,
- "Found enabled oem CredMan UI component."
- + oemComponentString);
- result = oemComponentName;
- } else {
- Slog.i(TAG,
- "Found enabled oem CredMan UI component but it was not "
- + "enabled.");
- }
- } catch (PackageManager.NameNotFoundException e) {
- Slog.i(TAG, "Unable to find oem CredMan UI component: "
- + oemComponentString + ".");
- }
+ String oemComponentString =
+ Resources.getSystem()
+ .getString(
+ com.android.internal.R.string
+ .config_oemCredentialManagerDialogComponent);
+ if (!TextUtils.isEmpty(oemComponentString)) {
+ ComponentName oemComponentName = ComponentName.unflattenFromString(
+ oemComponentString);
+ if (oemComponentName != null) {
+ try {
+ ActivityInfo info = context.getPackageManager().getActivityInfo(
+ oemComponentName,
+ PackageManager.ComponentInfoFlags.of(
+ PackageManager.MATCH_SYSTEM_ONLY));
+ if (info.enabled && info.exported) {
+ Slog.i(TAG,
+ "Found enabled oem CredMan UI component."
+ + oemComponentString);
+ result = oemComponentName;
} else {
- Slog.i(TAG, "Invalid OEM ComponentName format.");
+ Slog.i(TAG,
+ "Found enabled oem CredMan UI component but it was not "
+ + "enabled.");
}
- } else {
- Slog.i(TAG, "Invalid empty OEM component name.");
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.i(TAG, "Unable to find oem CredMan UI component: "
+ + oemComponentString + ".");
}
+ } else {
+ Slog.i(TAG, "Invalid OEM ComponentName format.");
}
+ } else {
+ Slog.i(TAG, "Invalid empty OEM component name.");
}
return result;
}
@@ -186,16 +189,11 @@ public class IntentFactory {
* Creates an Intent that cancels any UI matching the given request token id.
*/
@NonNull
- public static Intent createCancelUiIntent(@NonNull IBinder requestToken,
- boolean shouldShowCancellationUi, @NonNull String appPackageName) {
+ public static Intent createCancelUiIntent(@NonNull Context context,
+ @NonNull IBinder requestToken, boolean shouldShowCancellationUi,
+ @NonNull String appPackageName) {
Intent intent = new Intent();
- ComponentName componentName =
- ComponentName.unflattenFromString(
- Resources.getSystem()
- .getString(
- com.android.internal.R.string
- .config_credentialManagerDialogComponent));
- intent.setComponent(componentName);
+ setCredentialSelectorUiComponentName(context, intent);
intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST,
new CancelSelectionRequest(new RequestToken(requestToken), shouldShowCancellationUi,
appPackageName));
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index ecffe9e5a8b2..faa2c7018d6f 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -392,8 +392,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
return;
}
- Log.i(TAG, walFile.getAbsolutePath() + " " + size + " bytes: Bigger than "
- + threshold + "; truncating");
try {
executeForString("PRAGMA wal_checkpoint(TRUNCATE)", null, null);
mConfiguration.shouldTruncateWalFile = false;
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 5e523c0112b1..78c8954cfe5f 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -377,8 +377,7 @@ public abstract class SQLiteOpenHelper implements AutoCloseable {
if (writable) {
throw ex;
}
- Log.e(TAG, "Couldn't open " + mName
- + " for writing (will try read-only):", ex);
+ Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
db = SQLiteDatabase.openDatabase(filePath, params);
}
@@ -425,11 +424,6 @@ public abstract class SQLiteOpenHelper implements AutoCloseable {
}
onOpen(db);
-
- if (db.isReadOnly()) {
- Log.w(TAG, "Opened " + mName + " in read-only mode");
- }
-
mDatabase = db;
return db;
} finally {
diff --git a/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl b/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl
deleted file mode 100644
index 838e41ee1c08..000000000000
--- a/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) 2024, 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;
-
-/** @hide */
-parcelable CameraPrivacyAllowlistEntry {
- String packageName;
- boolean isMandatory;
-}
-
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index 851ce2add94f..19d10294a75a 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -16,7 +16,6 @@
package android.hardware;
-import android.hardware.CameraPrivacyAllowlistEntry;
import android.hardware.ISensorPrivacyListener;
/** @hide */
@@ -48,7 +47,7 @@ interface ISensorPrivacyManager {
void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)")
- List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+ List<String> getCameraPrivacyAllowlist();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)")
int getToggleSensorPrivacyState(int toggleType, int sensor);
@@ -62,6 +61,10 @@ interface ISensorPrivacyManager {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)")
boolean isCameraPrivacyEnabled(String packageName);
+ /** @hide */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)")
+ void setCameraPrivacyAllowlist(in List<String> allowlist);
+
// =============== End of transactions used on native side as well ============================
void suppressToggleSensorPrivacyReminders(int userId, int sensor, IBinder token,
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 6294a8d617de..08b906439b08 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -43,7 +43,7 @@ import com.android.internal.camera.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Map;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -204,6 +204,8 @@ public final class SensorPrivacyManager {
* Types of state which can exist for the sensor privacy toggle
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
public static class StateTypes {
private StateTypes() {}
@@ -217,30 +219,12 @@ public final class SensorPrivacyManager {
*/
public static final int DISABLED = SensorPrivacyIndividualEnabledSensorProto.DISABLED;
- /**
- * Constant indicating privacy is enabled except for the automotive driver assistance apps
- * which are helpful for driving.
- */
- @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS =
- SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_HELPFUL_APPS;
-
/**
* Constant indicating privacy is enabled except for the automotive driver assistance apps
* which are required by car manufacturer for driving.
*/
- @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS =
- SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_REQUIRED_APPS;
-
- /**
- * Constant indicating privacy is enabled except for the automotive driver assistance apps
- * which are both helpful for driving and also apps required by car manufacturer for
- * driving.
- */
- @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_APPS =
- SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_APPS;
+ public static final int ENABLED_EXCEPT_ALLOWLISTED_APPS =
+ SensorPrivacyIndividualEnabledSensorProto.ENABLED_EXCEPT_ALLOWLISTED_APPS;
/**
* Types of state which can exist for a sensor privacy toggle
@@ -250,9 +234,7 @@ public final class SensorPrivacyManager {
@IntDef(value = {
ENABLED,
DISABLED,
- AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS,
- AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS,
- AUTOMOTIVE_DRIVER_ASSISTANCE_APPS
+ ENABLED_EXCEPT_ALLOWLISTED_APPS
})
@Retention(RetentionPolicy.SOURCE)
public @interface StateType {}
@@ -369,9 +351,6 @@ public final class SensorPrivacyManager {
private final ArrayMap<Pair<Integer, OnSensorPrivacyChangedListener>,
OnSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
- @GuardedBy("mLock")
- private ArrayMap<String, Boolean> mCameraPrivacyAllowlist = null;
-
/** The singleton ISensorPrivacyListener for IPC which will be used to dispatch to local
* listeners */
@NonNull
@@ -397,7 +376,8 @@ public final class SensorPrivacyManager {
@Override
@FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- public void onSensorPrivacyStateChanged(int toggleType, int sensor, int state) {
+ public void onSensorPrivacyStateChanged(@ToggleType int toggleType,
+ @Sensors.Sensor int sensor, @StateTypes.StateType int state) {
synchronized (mLock) {
for (int i = 0; i < mToggleListeners.size(); i++) {
OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
@@ -725,6 +705,8 @@ public final class SensorPrivacyManager {
/**
* Returns sensor privacy state for a specific sensor.
*
+ * @param toggleType The type of toggle to use
+ * @param sensor The sensor to check
* @return int sensor privacy state.
*
* @hide
@@ -741,9 +723,10 @@ public final class SensorPrivacyManager {
}
}
- /**
+ /**
* Returns if camera privacy is enabled for a specific package.
*
+ * @param packageName The package to check
* @return boolean sensor privacy state.
*
* @hide
@@ -763,29 +746,41 @@ public final class SensorPrivacyManager {
* Returns camera privacy allowlist.
*
* @return List of automotive driver assistance packages for
- * privacy allowlisting. The returned map includes the package
- * name as key and the value is a Boolean which tells if that package
- * is required by the car manufacturer as mandatory package for driving.
+ * privacy allowlisting.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
@FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- public @NonNull Map<String, Boolean> getCameraPrivacyAllowlist() {
+ public @NonNull List<String> getCameraPrivacyAllowlist() {
synchronized (mLock) {
- if (mCameraPrivacyAllowlist == null) {
- mCameraPrivacyAllowlist = new ArrayMap<>();
- try {
- for (CameraPrivacyAllowlistEntry entry :
- mService.getCameraPrivacyAllowlist()) {
- mCameraPrivacyAllowlist.put(entry.packageName, entry.isMandatory);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ return mService.getCameraPrivacyAllowlist();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Sets camera privacy allowlist.
+ *
+ * @param allowlist List of automotive driver assistance packages for
+ * privacy allowlisting.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public void setCameraPrivacyAllowlist(@NonNull List<String> allowlist) {
+ synchronized (mLock) {
+ try {
+ mService.setCameraPrivacyAllowlist(allowlist);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return mCameraPrivacyAllowlist;
}
}
@@ -867,6 +862,7 @@ public final class SensorPrivacyManager {
/**
* Sets sensor privacy to the specified state for an individual sensor.
*
+ * @param source the source using which the sensor is toggled
* @param sensor the sensor which to change the state for
* @param state the state to which sensor privacy should be set.
*
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 57b437f4bacf..dc8f4b448931 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3521,7 +3521,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>When the key is present, only a PRIVATE/YUV output of the specified size is guaranteed
* to be supported by the camera HAL in the secure camera mode. Any other format or
* resolutions might not be supported. Use
- * {@link CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link CameraDevice#isSessionConfigurationSupported }
* API to query if a secure session configuration is supported if the device supports this
* API.</p>
* <p>If this key returns null on a device with SECURE_IMAGE_DATA capability, the application
@@ -5046,18 +5046,18 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>The version of the session configuration query
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
* API</p>
* <p>The possible values in this key correspond to the values defined in
* android.os.Build.VERSION_CODES. Each version defines a set of feature combinations the
* camera device must reliably report whether they are supported via
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
* API. And the version is always less or equal to android.os.Build.VERSION.SDK_INT.</p>
* <p>If set to UPSIDE_DOWN_CAKE, this camera device doesn't support
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }.
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }.
* Calling the method for this camera ID throws an UnsupportedOperationException.</p>
* <p>If set to VANILLA_ICE_CREAM, the application can call
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
* to check if the combinations of below features are supported.</p>
* <ul>
* <li>A subset of LIMITED-level device stream combinations.</li>
@@ -6082,11 +6082,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>Minimum and maximum padding zoom factors supported by this camera device for
- * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for the
+ * android.efv.paddingZoomFactor used for the
* {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
* extension.</p>
* <p>The minimum and maximum padding zoom factors supported by the device for
- * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the
+ * android.efv.paddingZoomFactor used as part of the
* {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
* extension feature. This extension specific camera characteristic can be queried using
* {@link android.hardware.camera2.CameraExtensionCharacteristics#get }.</p>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 7119730c6984..d2e4a614202f 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -753,20 +753,7 @@ public final class CameraManager {
@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
public CameraDevice.CameraDeviceSetup getCameraDeviceSetup(@NonNull String cameraId)
throws CameraAccessException {
- if (cameraId == null) {
- throw new IllegalArgumentException("cameraId was null");
- }
-
- if (CameraManagerGlobal.sCameraServiceDisabled) {
- throw new CameraAccessException(CameraAccessException.CAMERA_DISABLED,
- "No cameras available on device");
- }
-
- if (!Arrays.asList(CameraManagerGlobal.get().getCameraIdList()).contains(cameraId)) {
- throw new IllegalArgumentException(
- "Camera ID '" + cameraId + "' not available on device.");
- }
-
+ // isCameraDeviceSetup does all the error checking we need.
if (!isCameraDeviceSetupSupported(cameraId)) {
throw new UnsupportedOperationException(
"CameraDeviceSetup is not supported for Camera ID: " + cameraId);
@@ -819,12 +806,8 @@ public final class CameraManager {
throw new IllegalArgumentException("Camera ID was null");
}
- if (CameraManagerGlobal.sCameraServiceDisabled) {
- throw new CameraAccessException(CameraAccessException.CAMERA_DISABLED,
- "No cameras available on device");
- }
-
- if (!Arrays.asList(CameraManagerGlobal.get().getCameraIdList()).contains(cameraId)) {
+ if (CameraManagerGlobal.sCameraServiceDisabled
+ || !Arrays.asList(CameraManagerGlobal.get().getCameraIdList()).contains(cameraId)) {
throw new IllegalArgumentException(
"Camera ID '" + cameraId + "' not available on device.");
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e24c98e98c5d..9fb561bb4211 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -911,10 +911,10 @@ public abstract class CameraMetadata<TKey> {
* </ul>
* <p>Combinations of logical and physical streams, or physical streams from different
* physical cameras are not guaranteed. However, if the camera device supports
- * {@link CameraManager#isSessionConfigurationWithParametersSupported },
+ * {@link CameraDevice#isSessionConfigurationSupported },
* application must be able to query whether a stream combination involving physical
* streams is supported by calling
- * {@link CameraManager#isSessionConfigurationWithParametersSupported }.</p>
+ * {@link CameraDevice#isSessionConfigurationSupported }.</p>
* <p>Camera application shouldn't assume that there are at most 1 rear camera and 1 front
* camera in the system. For an application that switches between front and back cameras,
* the recommendation is to switch between the first rear camera and the first front
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 9fbe348f1e9a..f3b7b919d87d 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -260,7 +260,7 @@ public final class MandatoryStreamCombination {
* smaller sizes, then the resulting
* {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
* be tested either by calling {@link CameraDevice#createCaptureSession} or
- * {@link CameraManager#isSessionConfigurationWithParametersSupported}.
+ * {@link CameraDeviceSetup#isSessionConfigurationSupported}.
*
* @return non-modifiable ascending list of available sizes.
*/
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 95526a8affbb..8aacd5e3908f 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1379,7 +1379,6 @@ public final class OutputConfiguration implements Parcelable {
mSurfaceType != other.mSurfaceType ||
mIsDeferredConfig != other.mIsDeferredConfig ||
mIsShared != other.mIsShared ||
- mConfiguredFormat != other.mConfiguredFormat ||
mConfiguredDataspace != other.mConfiguredDataspace ||
mConfiguredGenerationId != other.mConfiguredGenerationId ||
!Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 1f5495999416..2816f777e8ab 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -27,6 +27,7 @@ import android.hardware.input.IKeyboardBacklightListener;
import android.hardware.input.IKeyboardBacklightState;
import android.hardware.input.IStickyModifierStateListener;
import android.hardware.input.ITabletModeChangedListener;
+import android.hardware.input.KeyboardLayoutSelectionResult;
import android.hardware.input.TouchCalibration;
import android.os.CombinedVibration;
import android.hardware.input.IInputSensorEventListener;
@@ -120,8 +121,9 @@ interface IInputManager {
String keyboardLayoutDescriptor);
// New Keyboard layout config APIs
- String getKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, int userId,
- in InputMethodInfo imeInfo, in InputMethodSubtype imeSubtype);
+ KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ in InputDeviceIdentifier identifier, int userId, in InputMethodInfo imeInfo,
+ in InputMethodSubtype imeSubtype);
@EnforcePermission("SET_KEYBOARD_LAYOUT")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 744dfae97108..a1242fb43bbd 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -784,10 +784,10 @@ public final class InputManager {
*
* @hide
*/
- @Nullable
- public String getKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
- @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
- @Nullable InputMethodSubtype imeSubtype) {
+ @NonNull
+ public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ @NonNull InputDeviceIdentifier identifier, @UserIdInt int userId,
+ @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
try {
return mIm.getKeyboardLayoutForInputDevice(identifier, userId, imeInfo, imeSubtype);
} catch (RemoteException ex) {
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/GoneSceneModule.kt b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl
index 5cc3b75df787..13be2ff5ffb7 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/GoneSceneModule.kt
+++ b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 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.
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.scene
+package android.hardware.input;
-import dagger.Module
-
-@Module interface GoneSceneModule
+parcelable KeyboardLayoutSelectionResult;
diff --git a/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java
new file mode 100644
index 000000000000..5a1c9478f629
--- /dev/null
+++ b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2024 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.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides information about the selected layout and the selection criteria when the caller calls
+ * {@link InputManager#getKeyboardLayoutForInputDevice(InputDeviceIdentifier, int, InputMethodInfo,
+ * InputMethodSubtype)}
+ *
+ * @hide
+ */
+
+@DataClass(genParcelable = true, genToString = true, genEqualsHashCode = true)
+public final class KeyboardLayoutSelectionResult implements Parcelable {
+ @Nullable
+ private final String mLayoutDescriptor;
+
+ /** Unspecified layout selection criteria */
+ public static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED = 0;
+
+ /** Manual selection by user */
+ public static final int LAYOUT_SELECTION_CRITERIA_USER = 1;
+
+ /** Auto-detection based on device provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 2;
+
+ /** Auto-detection based on IME provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 3;
+
+ /** Default selection */
+ public static final int LAYOUT_SELECTION_CRITERIA_DEFAULT = 4;
+
+ /** Failed layout selection */
+ public static final KeyboardLayoutSelectionResult FAILED = new KeyboardLayoutSelectionResult(
+ null, LAYOUT_SELECTION_CRITERIA_UNSPECIFIED);
+
+ @LayoutSelectionCriteria
+ private final int mSelectionCriteria;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @IntDef(prefix = "LAYOUT_SELECTION_CRITERIA_", value = {
+ LAYOUT_SELECTION_CRITERIA_UNSPECIFIED,
+ LAYOUT_SELECTION_CRITERIA_USER,
+ LAYOUT_SELECTION_CRITERIA_DEVICE,
+ LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ LAYOUT_SELECTION_CRITERIA_DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface LayoutSelectionCriteria {}
+
+ @DataClass.Generated.Member
+ public static String layoutSelectionCriteriaToString(@LayoutSelectionCriteria int value) {
+ switch (value) {
+ case LAYOUT_SELECTION_CRITERIA_UNSPECIFIED:
+ return "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED";
+ case LAYOUT_SELECTION_CRITERIA_USER:
+ return "LAYOUT_SELECTION_CRITERIA_USER";
+ case LAYOUT_SELECTION_CRITERIA_DEVICE:
+ return "LAYOUT_SELECTION_CRITERIA_DEVICE";
+ case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD:
+ return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD";
+ case LAYOUT_SELECTION_CRITERIA_DEFAULT:
+ return "LAYOUT_SELECTION_CRITERIA_DEFAULT";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ public KeyboardLayoutSelectionResult(
+ @Nullable String layoutDescriptor,
+ @LayoutSelectionCriteria int selectionCriteria) {
+ this.mLayoutDescriptor = layoutDescriptor;
+ this.mSelectionCriteria = selectionCriteria;
+
+ if (!(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_UNSPECIFIED)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT)) {
+ throw new java.lang.IllegalArgumentException(
+ "selectionCriteria was " + mSelectionCriteria + " but must be one of: "
+ + "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED(" + LAYOUT_SELECTION_CRITERIA_UNSPECIFIED + "), "
+ + "LAYOUT_SELECTION_CRITERIA_USER(" + LAYOUT_SELECTION_CRITERIA_USER + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEVICE(" + LAYOUT_SELECTION_CRITERIA_DEVICE + "), "
+ + "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD(" + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEFAULT(" + LAYOUT_SELECTION_CRITERIA_DEFAULT + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getLayoutDescriptor() {
+ return mLayoutDescriptor;
+ }
+
+ @DataClass.Generated.Member
+ public @LayoutSelectionCriteria int getSelectionCriteria() {
+ return mSelectionCriteria;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "KeyboardLayoutSelectionResult { " +
+ "layoutDescriptor = " + mLayoutDescriptor + ", " +
+ "selectionCriteria = " + layoutSelectionCriteriaToString(mSelectionCriteria) +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(KeyboardLayoutSelectionResult other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ KeyboardLayoutSelectionResult that = (KeyboardLayoutSelectionResult) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mLayoutDescriptor, that.mLayoutDescriptor)
+ && mSelectionCriteria == that.mSelectionCriteria;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mLayoutDescriptor);
+ _hash = 31 * _hash + mSelectionCriteria;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mLayoutDescriptor != null) flg |= 0x1;
+ dest.writeByte(flg);
+ if (mLayoutDescriptor != null) dest.writeString(mLayoutDescriptor);
+ dest.writeInt(mSelectionCriteria);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ KeyboardLayoutSelectionResult(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String layoutDescriptor = (flg & 0x1) == 0 ? null : in.readString();
+ int selectionCriteria = in.readInt();
+
+ this.mLayoutDescriptor = layoutDescriptor;
+ this.mSelectionCriteria = selectionCriteria;
+
+ if (!(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_UNSPECIFIED)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT)) {
+ throw new java.lang.IllegalArgumentException(
+ "selectionCriteria was " + mSelectionCriteria + " but must be one of: "
+ + "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED(" + LAYOUT_SELECTION_CRITERIA_UNSPECIFIED + "), "
+ + "LAYOUT_SELECTION_CRITERIA_USER(" + LAYOUT_SELECTION_CRITERIA_USER + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEVICE(" + LAYOUT_SELECTION_CRITERIA_DEVICE + "), "
+ + "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD(" + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEFAULT(" + LAYOUT_SELECTION_CRITERIA_DEFAULT + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<KeyboardLayoutSelectionResult> CREATOR
+ = new Parcelable.Creator<KeyboardLayoutSelectionResult>() {
+ @Override
+ public KeyboardLayoutSelectionResult[] newArray(int size) {
+ return new KeyboardLayoutSelectionResult[size];
+ }
+
+ @Override
+ public KeyboardLayoutSelectionResult createFromParcel(@NonNull android.os.Parcel in) {
+ return new KeyboardLayoutSelectionResult(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1709568115865L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java",
+ inputSignatures = "private final @android.annotation.Nullable java.lang.String mLayoutDescriptor\npublic static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED\npublic static final int LAYOUT_SELECTION_CRITERIA_USER\npublic static final int LAYOUT_SELECTION_CRITERIA_DEVICE\npublic static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD\npublic static final int LAYOUT_SELECTION_CRITERIA_DEFAULT\npublic static final android.hardware.input.KeyboardLayoutSelectionResult FAILED\nprivate final @android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria int mSelectionCriteria\nclass KeyboardLayoutSelectionResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 63ae28f6ff9d..2a118350610f 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -20,3 +20,10 @@ flag {
description: "Enable reporting USB data compliance warnings from HAL when set"
bug: "296119135"
}
+
+flag {
+ name: "enable_usb_data_signal_staking"
+ namespace: "preload_safety"
+ description: "Enables signal API with staking"
+ bug: "296119135"
+} \ No newline at end of file
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index f5b58b920efb..9dc8c5d9f2e9 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -453,7 +453,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ public void showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
@@ -462,7 +462,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
+ public void hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_HIDE_SOFT_INPUT,
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2c7ca27e6b07..4dbdd91d5fc7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -701,7 +701,13 @@ public class InputMethodService extends AbstractInputMethodService {
*/
private IBinder mCurHideInputToken;
- /** The token tracking the current IME request or {@code null} otherwise. */
+ /**
+ * The token tracking the current IME request.
+ *
+ * <p> This exists as a workaround to changing the signatures of public methods. It will get
+ * set to a {@code non-null} value before every call that uses it, stored locally inside the
+ * callee, and immediately after reset to {@code null} from the callee.
+ */
@Nullable
private ImeTracker.Token mCurStatsToken;
@@ -907,14 +913,13 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) {
+ IBinder hideInputToken, @NonNull ImeTracker.Token statsToken) {
mSystemCallingHideSoftInput = true;
mCurHideInputToken = hideInputToken;
mCurStatsToken = statsToken;
try {
hideSoftInput(flags, resultReceiver);
} finally {
- mCurStatsToken = null;
mCurHideInputToken = null;
mSystemCallingHideSoftInput = false;
}
@@ -926,23 +931,33 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- ImeTracker.forLogging().onProgress(
- mCurStatsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "hideSoftInput()");
+
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(false /* show */,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
+ // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
&& !mSystemCallingHideSoftInput) {
Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
+ " Use requestHideSelf(int) itself");
+ ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
+
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
mShowInputFlags = 0;
mShowInputRequested = false;
+ mCurStatsToken = statsToken;
hideWindow();
final boolean isVisible = isInputViewShown();
final boolean visibilityChanged = isVisible != wasVisible;
@@ -963,14 +978,13 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public void showSoftInputWithToken(@InputMethod.ShowFlags int flags,
ResultReceiver resultReceiver, IBinder showInputToken,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
mSystemCallingShowSoftInput = true;
mCurShowInputToken = showInputToken;
mCurStatsToken = statsToken;
try {
showSoftInput(flags, resultReceiver);
} finally {
- mCurStatsToken = null;
mCurShowInputToken = null;
mSystemCallingShowSoftInput = false;
}
@@ -982,16 +996,23 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
- ImeTracker.forLogging().onProgress(
- mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "showSoftInput()");
+
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(true /* show */,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
// TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
&& !mSystemCallingShowSoftInput) {
- Log.e(TAG," IME shouldn't call showSoftInput on itself."
+ Log.e(TAG, "IME shouldn't call showSoftInput on itself."
+ " Use requestShowSelf(int) itself");
+ ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
ImeTracing.getInstance().triggerServiceDump(
@@ -999,11 +1020,12 @@ public class InputMethodService extends AbstractInputMethodService {
null /* icProto */);
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
- ImeTracker.forLogging().onProgress(mCurStatsToken,
+ ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
- showWindow(true);
+ mCurStatsToken = statsToken;
+ showWindow(true /* showInput */);
} else {
- ImeTracker.forLogging().onFailed(mCurStatsToken,
+ ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
}
setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
@@ -1895,21 +1917,23 @@ public class InputMethodService extends AbstractInputMethodService {
if (showingInput) {
// If we were last showing the soft keyboard, try to do so again.
if (dispatchOnShowInputRequested(showFlags, true)) {
- showWindow(true);
+ showWindowWithToken(true /* showInput */,
+ SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
if (completions != null) {
mCurCompletions = completions;
onDisplayCompletions(completions);
}
} else {
- hideWindow();
+ hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
}
} else if (mCandidatesVisibility == View.VISIBLE) {
// If the candidates are currently visible, make sure the
// window is shown for them.
- showWindow(false);
+ showWindowWithToken(false /* showInput */,
+ SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
} else {
// Otherwise hide the window.
- hideWindow();
+ hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
@@ -2368,13 +2392,15 @@ public class InputMethodService extends AbstractInputMethodService {
// has not asked for the input view to be shown, then we need
// to update whether the window is shown.
if (shown) {
- showWindow(false);
+ showWindowWithToken(false /* showInput */,
+ SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY);
} else {
- hideWindow();
+ hideWindowWithToken(
+ SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY);
}
}
}
-
+
void updateCandidatesVisibility(boolean shown) {
int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
if (mCandidatesVisibility != vis) {
@@ -3009,6 +3035,19 @@ public class InputMethodService extends AbstractInputMethodService {
return result;
}
+ /**
+ * Utility function that creates an IME request tracking token before
+ * calling {@link #showWindow}.
+ *
+ * @param showInput whether the input window should be shown.
+ * @param reason the reason why the IME request was created.
+ */
+ private void showWindowWithToken(boolean showInput, @SoftInputShowHideReason int reason) {
+ mCurStatsToken = createStatsToken(true /* show */, reason,
+ ImeTracker.isFromUser(mRootView));
+ showWindow(showInput);
+ }
+
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
@@ -3018,11 +3057,20 @@ public class InputMethodService extends AbstractInputMethodService {
+ " mInputStarted=" + mInputStarted
+ " mShowInputFlags=" + mShowInputFlags);
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(true /* show */,
+ SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
if (mInShowWindow) {
Log.w(TAG, "Re-entrance in to showWindow");
+ ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW);
+
ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
null /* icProto */);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
@@ -3046,7 +3094,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
mWindow.show();
mDecorViewWasVisible = true;
- applyVisibilityInInsetsConsumerIfNecessary(true);
+ applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */, statsToken);
cancelImeSurfaceRemoval();
mInShowWindow = false;
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3137,13 +3185,15 @@ public class InputMethodService extends AbstractInputMethodService {
* Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}.
*
* @param setVisible {@code true} to make it visible, false to hide it.
+ * @param statsToken the token tracking the current IME request.
*/
- private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
+ private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible,
+ @NonNull ImeTracker.Token statsToken) {
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
null /* icProto */);
mPrivOps.applyImeVisibilityAsync(setVisible
- ? mCurShowInputToken : mCurHideInputToken, setVisible, mCurStatsToken);
+ ? mCurShowInputToken : mCurHideInputToken, setVisible, statsToken);
}
private void finishViews(boolean finishingInput) {
@@ -3159,12 +3209,35 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesViewStarted = false;
}
+ /**
+ * Utility function that creates an IME request tracking token before
+ * calling {@link #hideWindow}.
+ *
+ * @param reason the reason why the IME request was created.
+ */
+ private void hideWindowWithToken(@SoftInputShowHideReason int reason) {
+ // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it
+ // to work with onClickListeners
+ final boolean isFromUser = ImeTracker.isFromUser(mRootView)
+ || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
+ mCurStatsToken = createStatsToken(false /* show */, reason, isFromUser);
+ hideWindow();
+ }
+
public void hideWindow() {
if (DEBUG) Log.v(TAG, "CALL: hideWindow");
+
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(false /* show */,
+ SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
setImeWindowStatus(0, mBackDisposition);
- applyVisibilityInInsetsConsumerIfNecessary(false);
+ applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */, statsToken);
mWindowVisible = false;
finishViews(false /* finishingInput */);
if (mDecorViewVisible) {
@@ -3440,9 +3513,14 @@ public class InputMethodService extends AbstractInputMethodService {
private void requestHideSelf(@InputMethodManager.HideFlags int flags,
@SoftInputShowHideReason int reason) {
+ // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it
+ // to work with onClickListeners
+ final boolean isFromUser = ImeTracker.isFromUser(mRootView)
+ || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
+ final var statsToken = createStatsToken(false /* show */, reason, isFromUser);
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
null /* icProto */);
- mPrivOps.hideMySoftInput(flags, reason);
+ mPrivOps.hideMySoftInput(statsToken, flags, reason);
}
/**
@@ -3450,9 +3528,16 @@ public class InputMethodService extends AbstractInputMethodService {
* interact with it.
*/
public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) {
+ requestShowSelf(flags, SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME);
+ }
+
+ private void requestShowSelf(@InputMethodManager.ShowFlags int flags,
+ @SoftInputShowHideReason int reason) {
+ final var statsToken = createStatsToken(true /* show */, reason,
+ ImeTracker.isFromUser(mRootView));
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
null /* icProto */);
- mPrivOps.showMySoftInput(flags);
+ mPrivOps.showMySoftInput(statsToken, flags, reason);
}
private boolean handleBack(boolean doIt) {
@@ -3472,7 +3557,7 @@ public class InputMethodService extends AbstractInputMethodService {
// If we have the window visible for some other reason --
// most likely to show candidates -- then just get rid
// of it. This really shouldn't happen, but just in case...
- if (doIt) hideWindow();
+ if (doIt) hideWindowWithToken(SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY);
}
return true;
}
@@ -3627,10 +3712,11 @@ public class InputMethodService extends AbstractInputMethodService {
@InputMethodManager.HideFlags int hideFlags) {
if (DEBUG) Log.v(TAG, "toggleSoftInput()");
if (isInputViewShown()) {
- requestHideSelf(
- hideFlags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
+ requestHideSelf(hideFlags,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
} else {
- requestShowSelf(showFlags);
+ requestShowSelf(showFlags,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
}
}
@@ -4272,6 +4358,20 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * Creates an IME request tracking token.
+ *
+ * @param show whether this is a show or a hide request.
+ * @param reason the reason why the IME request was created.
+ * @param isFromUser whether this request was created directly from user interaction.
+ */
+ @NonNull
+ private ImeTracker.Token createStatsToken(boolean show, @SoftInputShowHideReason int reason,
+ boolean isFromUser) {
+ return ImeTracker.forLogging().onStart(show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_IME, reason, isFromUser);
+ }
+
+ /**
* Performs a dump of the InputMethodService's internal state. Override
* to add your own information to the dump.
*/
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 05a1abeaf479..b41753413baf 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -240,7 +240,7 @@ public abstract class BatteryConsumer {
new Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_ANY);
/**
- * Identifies power attribution dimensions that are captured by an data element of
+ * Identifies power attribution dimensions that are captured by a data element of
* a BatteryConsumer. These Keys are used to access those values and to set them using
* Builders. See for example {@link #getConsumedPower(Key)}.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e09094203ad4..c611cb972b2c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1587,7 +1587,7 @@ public abstract class BatteryStats {
outNumOfInterest[0] = numOfInterest;
}
- // The estimated time is the average time we spend in each level, multipled
+ // The estimated time is the average time we spend in each level, multiplied
// by 100 -- the total number of battery levels
return (total / numOfInterest) * 100;
}
@@ -2019,7 +2019,7 @@ public abstract class BatteryStats {
public static final int EVENT_TOP = 0x0003;
// Event is about active sync operations.
public static final int EVENT_SYNC = 0x0004;
- // Events for all additional wake locks aquired/release within a wake block.
+ // Events for all additional wake locks acquired/release within a wake block.
// These are not generated by default.
public static final int EVENT_WAKE_LOCK = 0x0005;
// Event is about an application executing a scheduled job.
@@ -3419,7 +3419,7 @@ public abstract class BatteryStats {
* incoming service calls from apps. The result is returned as an array of longs,
* organized as a sequence like this:
* <pre>
- * cluster1-speeed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
+ * cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
* </pre>
*
* @see com.android.internal.os.CpuScalingPolicies#getPolicies
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 3149de4c39e7..beb9a935a6ee 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -120,7 +120,6 @@ public class GraphicsEnvironment {
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
private String mLibraryPermittedPaths;
- private GameManager mGameManager;
private int mAngleOptInIndex = -1;
private boolean mShouldUseAngle = false;
@@ -134,8 +133,6 @@ public class GraphicsEnvironment {
final ApplicationInfo appInfoWithMetaData =
getAppInfoWithMetadata(context, pm, packageName);
- mGameManager = context.getSystemService(GameManager.class);
-
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
@@ -161,9 +158,11 @@ public class GraphicsEnvironment {
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "notifyGraphicsEnvironmentSetup");
- if (mGameManager != null
- && appInfoWithMetaData.category == ApplicationInfo.CATEGORY_GAME) {
- mGameManager.notifyGraphicsEnvironmentSetup();
+ if (appInfoWithMetaData.category == ApplicationInfo.CATEGORY_GAME) {
+ final GameManager gameManager = context.getSystemService(GameManager.class);
+ if (gameManager != null) {
+ gameManager.notifyGraphicsEnvironmentSetup();
+ }
}
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 9fd37d4548ac..fb500a96c737 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -618,7 +618,7 @@ public class HwParcel {
*/
@FastNative
@NonNull
- public native final @Nullable
+ public native final
HidlMemory readEmbeddedHidlMemory(long fieldHandle, long parentHandle, long offset);
/**
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index 65e498e14475..8b1577cb4b1c 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -41,5 +41,5 @@ interface IVibratorManagerService {
// There is no order guarantee with respect to the two-way APIs above like
// vibrate/isVibrating/cancel.
oneway void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
- boolean always, String reason);
+ boolean always, String reason, boolean fromIme);
}
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 3950c25675d8..2fe115f49099 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -623,14 +623,14 @@ public final class MessageQueue {
// Message is to be inserted at tail or middle of queue. Usually we don't have to
// wake up the event queue unless there is a barrier at the head of the queue and
// the message is the earliest asynchronous message in the queue.
- //
+ needWake = mBlocked && p.target == null && msg.isAsynchronous();
+
// For readability, we split this portion of the function into two blocks based on
// whether tail tracking is enabled. This has a minor implication for the case
// where tail tracking is disabled. See the comment below.
if (Flags.messageQueueTailTracking()) {
- needWake = mBlocked && p.target == null && msg.isAsynchronous()
- && mAsyncMessageCount == 0;
if (when >= mLast.when) {
+ needWake = needWake && mAsyncMessageCount == 0;
msg.next = null;
mLast.next = msg;
mLast = msg;
@@ -643,6 +643,9 @@ public final class MessageQueue {
if (p == null || when < p.when) {
break;
}
+ if (needWake && p.isAsynchronous()) {
+ needWake = false;
+ }
}
if (p == null) {
/* Inserting at tail of queue */
@@ -652,7 +655,6 @@ public final class MessageQueue {
prev.next = msg;
}
} else {
- needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 04c257b92e29..2a62c24a86e1 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -206,12 +206,13 @@ public class SystemVibrator extends Vibrator {
}
@Override
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(
+ int constant, boolean always, String reason, boolean fromIme) {
if (mVibratorManager == null) {
Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager.");
return;
}
- mVibratorManager.performHapticFeedback(constant, always, reason);
+ mVibratorManager.performHapticFeedback(constant, always, reason, fromIme);
}
@Override
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index 8e8392302824..c80bcac2624f 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -147,14 +147,15 @@ public class SystemVibratorManager extends VibratorManager {
}
@Override
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(int constant, boolean always, String reason,
+ boolean fromIme) {
if (mService == null) {
Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager service.");
return;
}
try {
mService.performHapticFeedback(
- mUid, mContext.getDeviceId(), mPackageName, constant, always, reason);
+ mUid, mContext.getDeviceId(), mPackageName, constant, always, reason, fromIme);
} catch (RemoteException e) {
Log.w(TAG, "Failed to perform haptic feedback.", e);
}
@@ -244,8 +245,9 @@ public class SystemVibratorManager extends VibratorManager {
}
@Override
- public void performHapticFeedback(int effectId, boolean always, String reason) {
- SystemVibratorManager.this.performHapticFeedback(effectId, always, reason);
+ public void performHapticFeedback(int effectId, boolean always, String reason,
+ boolean fromIme) {
+ SystemVibratorManager.this.performHapticFeedback(effectId, always, reason, fromIme);
}
@Override
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ccb534eb1019..9757a1096a30 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4708,6 +4708,9 @@ public class UserManager {
* Sets the user as enabled, if such an user exists.
*
* <p>Note that the default is true, it's only that managed profiles might not be enabled.
+ * (Managed profiles created by DevicePolicyManager will start out disabled, and DPM will later
+ * toggle them to enabled once they are provisioned. This is the primary purpose of the
+ * {@link UserInfo#FLAG_DISABLED} flag.)
* Also ephemeral users can be disabled to indicate that their removal is in progress and they
* shouldn't be re-entered. Therefore ephemeral users should not be re-enabled once disabled.
*
@@ -5259,7 +5262,7 @@ public class UserManager {
/**
* Returns list of the profiles of userId including userId itself.
- * Note that this returns only enabled.
+ * Note that this returns only {@link UserInfo#isEnabled() enabled} profiles.
* <p>Note that this includes all profile types (not including Restricted profiles).
*
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 46705a31f395..9df5b850188f 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -289,6 +289,15 @@ public final class VibrationAttributes implements Parcelable {
}
/**
+ * Return the original {@link AudioAttributes} used to create the vibration attributes.
+ * @hide
+ */
+ @AudioAttributes.AttributeUsage
+ public int getOriginalAudioUsage() {
+ return mOriginalAudioUsage;
+ }
+
+ /**
* Return the flags.
* @return a combined mask of all flags
*/
@@ -405,8 +414,8 @@ public final class VibrationAttributes implements Parcelable {
return "VibrationAttributes{"
+ "mUsage=" + usageToString()
+ ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
- + ", mFlags=" + mFlags
+ ", mCategory=" + categoryToString()
+ + ", mFlags=" + mFlags
+ '}';
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 2fc24142acf2..4b2d4eb833ff 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -534,10 +534,12 @@ public abstract class Vibrator {
* {@code false} if the vibration for the haptic feedback should respect the applicable
* vibration intensity settings.
* @param reason the reason for this haptic feedback.
+ * @param fromIme the haptic feedback is performed from an IME.
*
* @hide
*/
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(int constant, boolean always, String reason,
+ boolean fromIme) {
Log.w(TAG, "performHapticFeedback is not supported");
}
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index e0b6a9fd28f0..513c4bd7ec0c 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -146,9 +146,11 @@ public abstract class VibratorManager {
* vibration intensity settings applicable to the corresponding vibration.
* {@code false} otherwise.
* @param reason the reason for this haptic feedback.
+ * @param fromIme the haptic feedback is performed from an IME.
* @hide
*/
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(int constant, boolean always, String reason,
+ boolean fromIme) {
Log.w(TAG, "performHapticFeedback is not supported");
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index abfa4e3dd8dc..d9400accb4bd 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -1,4 +1,5 @@
package: "android.os"
+container: "system"
flag {
name: "android_os_build_vanilla_ice_cream"
@@ -40,6 +41,7 @@ flag {
namespace: "profile_experiences"
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
bug: "299069460"
+ is_exported: true
}
flag {
diff --git a/core/java/android/os/health/HealthStatsWriter.java b/core/java/android/os/health/HealthStatsWriter.java
index d4d10b056c5c..4118775af28a 100644
--- a/core/java/android/os/health/HealthStatsWriter.java
+++ b/core/java/android/os/health/HealthStatsWriter.java
@@ -58,7 +58,7 @@ public class HealthStatsWriter {
* Construct a HealthStatsWriter object with the given constants.
*
* The "getDataType()" of the resulting HealthStats object will be the
- * short name of the java class that the Constants object was initalized
+ * short name of the java class that the Constants object was initialized
* with.
*/
public HealthStatsWriter(HealthKeys.Constants constants) {
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 88096ab91261..ada708bda34c 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -52,7 +52,7 @@ import java.util.concurrent.Executor;
*
* After the installation is completed, the device will be running in the new system on next the
* reboot. Then, when the user reboots the device again, it will leave {@code DynamicSystem} and go
- * back to the original system. While running in {@code DynamicSystem}, persitent storage for
+ * back to the original system. While running in {@code DynamicSystem}, persistent storage for
* factory reset protection (FRP) remains unchanged. Since the user is running the new system with
* a temporarily created data partition, their original user data are kept unchanged.</p>
*
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 536795bafb1c..8ce87e97da9c 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -172,7 +172,7 @@ public class DynamicSystemManager {
}
}
/**
- * Finish a previously started installation. Installations without a cooresponding
+ * Finish a previously started installation. Installations without a corresponding
* finishInstallation() will be cleaned up during device boot.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5a095410bef6..d45a17f7194e 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1395,7 +1395,7 @@ public class StorageManager {
// Package name can be null if the activity thread is running but the app
// hasn't bound yet. In this case we fall back to the first package in the
// current UID. This works for runtime permissions as permission state is
- // per UID and permission realted app ops are updated for all UID packages.
+ // per UID and permission related app ops are updated for all UID packages.
String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
android.os.Process.myUid());
if (packageNames == null || packageNames.length <= 0) {
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index e1f112af41e7..4cf2fd4dce69 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -62,7 +62,7 @@ import java.util.UUID;
* <li>To get access to standard directories (like the {@link Environment#DIRECTORY_PICTURES}), they
* can use the {@link #createAccessIntent(String)}. This is the recommend way, since it provides a
* simpler API and narrows the access to the given directory (and its descendants).
- * <li>To get access to any directory (and its descendants), they can use the Storage Acess
+ * <li>To get access to any directory (and its descendants), they can use the Storage Access
* Framework APIs (such as {@link Intent#ACTION_OPEN_DOCUMENT} and
* {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, although these APIs do not guarantee the user will
* select this specific volume.
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index bcdb98282679..a14a2c7e54ca 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -224,17 +224,19 @@ public class VibrationConfig {
@Override
public String toString() {
return "VibrationConfig{"
- + "mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+ + "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger
+ + ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+ ", mRampStepDurationMs=" + mRampStepDurationMs
+ ", mRampDownDurationMs=" + mRampDownDurationMs
+ + ", mRequestVibrationParamsForUsages="
+ + Arrays.toString(getRequestVibrationParamsForUsagesNames())
+ + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs
+ ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity
+ ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity
+ ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity
+ ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
+ ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
- + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs
- + ", mRequestVibrationParamsForUsages=" + Arrays.toString(
- getRequestVibrationParamsForUsagesNames())
+ + ", mDefaultKeyboardVibrationEnabled=" + mDefaultKeyboardVibrationEnabled
+ "}";
}
@@ -246,9 +248,13 @@ public class VibrationConfig {
public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) {
pw.println("VibrationConfig:");
pw.increaseIndent();
+ pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger);
pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
pw.println("rampStepDurationMs = " + mRampStepDurationMs);
pw.println("rampDownDurationMs = " + mRampDownDurationMs);
+ pw.println("requestVibrationParamsForUsages = "
+ + Arrays.toString(getRequestVibrationParamsForUsagesNames()));
+ pw.println("requestVibrationParamsTimeoutMs = " + mRequestVibrationParamsTimeoutMs);
pw.decreaseIndent();
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 8495f3747573..410f51045b9d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -247,8 +247,6 @@ public final class PermissionManager {
private final LegacyPermissionManager mLegacyPermissionManager;
- private final VirtualDeviceManager mVirtualDeviceManager;
-
private final ArrayMap<PackageManager.OnPermissionsChangedListener,
IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>();
private PermissionUsageHelper mUsageHelper;
@@ -269,7 +267,6 @@ public final class PermissionManager {
mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
"permissionmgr"));
mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
- mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class);
}
/**
@@ -1791,6 +1788,9 @@ public final class PermissionManager {
/**
* Gets the permission states for requested package and persistent device.
+ * <p>
+ * <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the
+ * exact permission states for the requested device.
*
* @param packageName name of the package you are checking against
* @param persistentDeviceId id of the persistent device you are checking against
@@ -1918,14 +1918,18 @@ public final class PermissionManager {
if (deviceId == Context.DEVICE_ID_DEFAULT) {
persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
} else if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
- VirtualDevice virtualDevice = mVirtualDeviceManager.getVirtualDevice(deviceId);
- if (virtualDevice == null) {
- Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
- return null;
- }
- persistentDeviceId = virtualDevice.getPersistentDeviceId();
- if (persistentDeviceId == null) {
- Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+ VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
+ VirtualDeviceManager.class);
+ if (virtualDeviceManager != null) {
+ VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+ if (virtualDevice == null) {
+ Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
+ return null;
+ }
+ persistentDeviceId = virtualDevice.getPersistentDeviceId();
+ if (persistentDeviceId == null) {
+ Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+ }
}
} else {
Slog.e(LOG_TAG, "vdmPublicApis flag is not enabled when device Id " + deviceId
@@ -2072,5 +2076,29 @@ public final class PermissionManager {
return new PermissionState[size];
}
};
+
+ /** @hide */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PermissionState that = (PermissionState) o;
+ return mGranted == that.mGranted && mFlags == that.mFlags;
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGranted, mFlags);
+ }
+
+ /** @hide */
+ @Override
+ public String toString() {
+ return "PermissionState{"
+ + "mGranted=" + mGranted
+ + ", mFlags=" + mFlags
+ + '}';
+ }
}
}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index de7008b19f54..dc782d4e1e9b 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -138,3 +138,11 @@ flag {
bug: "325356776"
}
+flag {
+ name: "runtime_permission_appops_mapping_enabled"
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "Use runtime permission state to determine appop state"
+ bug: "266164193"
+}
+
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index c03dc718d1a1..120846ca593b 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -667,7 +667,8 @@ public class CallLog {
@FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER)
public @NonNull AddCallParametersBuilder setAssertedDisplayName(
String assertedDisplayName) {
- if (assertedDisplayName.length() > MAX_NUMBER_OF_CHARACTERS) {
+ if (assertedDisplayName != null
+ && assertedDisplayName.length() > MAX_NUMBER_OF_CHARACTERS) {
throw new IllegalArgumentException("assertedDisplayName exceeds the character"
+ " limit of " + MAX_NUMBER_OF_CHARACTERS + ".");
}
diff --git a/core/java/android/provider/E2eeContactKeysManager.java b/core/java/android/provider/E2eeContactKeysManager.java
index b69441700b0a..09c93e383b5c 100644
--- a/core/java/android/provider/E2eeContactKeysManager.java
+++ b/core/java/android/provider/E2eeContactKeysManager.java
@@ -76,12 +76,14 @@ import java.util.Objects;
public final class E2eeContactKeysManager {
/**
* The authority for the end-to-end encryption contact keys provider.
+ *
* @hide
*/
public static final String AUTHORITY = "com.android.contactkeys.contactkeysprovider";
/**
* A content:// style uri to the authority for the end-to-end encryption contact keys provider.
+ *
* @hide
*/
@NonNull
@@ -112,9 +114,9 @@ public final class E2eeContactKeysManager {
* The inserted/updated end-to-end encryption contact key is owned by the caller app.
*
* @param lookupKey value that references the contact
- * @param deviceId an app-specified identifier for the device
+ * @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
- * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes)
+ * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes)
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
public void updateOrInsertE2eeContactKey(@NonNull String lookupKey,
@@ -138,9 +140,8 @@ public final class E2eeContactKeysManager {
* accountId and inferred caller package name.
*
* @param lookupKey the value that references the contact
- * @param deviceId an app-specified identifier for the device
+ * @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
- *
* @return a {@link E2eeContactKey} object containing the contact key information,
* or null if no contact key is found.
*/
@@ -170,7 +171,6 @@ public final class E2eeContactKeysManager {
* The keys will be stripped of deviceId, timeUpdated and keyValue data.
*
* @param lookupKey the value that references the contact
- *
* @return a list of {@link E2eeContactKey} objects containing the contact key
* information, or an empty list if no keys are found.
*/
@@ -199,7 +199,6 @@ public final class E2eeContactKeysManager {
* the caller app.
*
* @param lookupKey the value that references the contact
- *
* @return a list of {@link E2eeContactKey} objects containing the end-to-end encryption
* contact key information, or an empty list if no keys are found.
*/
@@ -227,11 +226,10 @@ public final class E2eeContactKeysManager {
* Updates an end-to-end encryption contact key entry's local verification state that belongs
* to the caller app.
*
- * @param lookupKey the value that references the contact
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
+ * @param lookupKey the value that references the contact
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
* @param localVerificationState the new local verification state
- *
* @return true if the entry was updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
@@ -257,14 +255,12 @@ public final class E2eeContactKeysManager {
* Updates an end-to-end encryption contact key entry's local verification state that belongs
* to the app identified by ownerPackageName.
*
- * @param lookupKey the value that references the contact
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
- * @param ownerPackageName the package name of the app that owns the key
+ * @param lookupKey the value that references the contact
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
+ * @param ownerPackageName the package name of the app that owns the key
* @param localVerificationState the new local verification state
- *
* @return true if the entry was updated, false otherwise.
- *
* @hide
*/
@SystemApi
@@ -296,11 +292,10 @@ public final class E2eeContactKeysManager {
* Updates an end-to-end encryption contact key entry's remote verification state that belongs
* to the caller app.
*
- * @param lookupKey the value that references the contact
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
+ * @param lookupKey the value that references the contact
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
* @param remoteVerificationState the new remote verification state
- *
* @return true if the entry was updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
@@ -326,14 +321,12 @@ public final class E2eeContactKeysManager {
* Updates an end-to-end encryption contact key entry's remote verification state that belongs
* to the app identified by ownerPackageName.
*
- * @param lookupKey the value that references the contact
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
- * @param ownerPackageName the package name of the app that owns the key
+ * @param lookupKey the value that references the contact
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
+ * @param ownerPackageName the package name of the app that owns the key
* @param remoteVerificationState the new remote verification state
- *
* @return true if the entry was updated, false otherwise.
- *
* @hide
*/
@SystemApi
@@ -374,9 +367,8 @@ public final class E2eeContactKeysManager {
* Removes an end-to-end encryption contact key entry that belongs to the caller app.
*
* @param lookupKey the value that references the contact
- * @param deviceId an app-specified identifier for the device
+ * @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
- *
* @return true if the entry was removed, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
@@ -397,11 +389,10 @@ public final class E2eeContactKeysManager {
/**
* Inserts a new entry into the end-to-end encryption self keys table or updates one if it
* already exists.
-
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
- * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes)
*
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
+ * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes)
* @return true if the entry was added or updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
@@ -432,10 +423,9 @@ public final class E2eeContactKeysManager {
/**
* Updates an end-to-end encryption self key entry's remote verification state.
*
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
* @param remoteVerificationState the new remote verification state
- *
* @return true if the entry was updated, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
@@ -459,13 +449,11 @@ public final class E2eeContactKeysManager {
* Updates an end-to-end encryption self key entry's remote verification state that belongs to
* the app identified by ownerPackageName.
*
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
- * @param ownerPackageName the package name of the app that owns the key
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
+ * @param ownerPackageName the package name of the app that owns the key
* @param remoteVerificationState the new remote verification state
- *
* @return true if the entry was updated, false otherwise.
- *
* @hide
*/
@SystemApi
@@ -502,9 +490,8 @@ public final class E2eeContactKeysManager {
* Returns an end-to-end encryption self key entry given the deviceId and the inferred package
* name of the caller.
*
- * @param deviceId an app-specified identifier for the device
+ * @param deviceId an app-specified identifier for the device
* @param accountId an app-specified identifier for the account
- *
* @return a {@link E2eeSelfKey} object containing the end-to-end encryption self key
* information, or null if no self key is found.
*/
@@ -579,9 +566,9 @@ public final class E2eeContactKeysManager {
/**
* Removes an end-to-end encryption self key entry given the deviceId and the inferred
* package name of the caller.
- * @param deviceId an app-specified identifier for the device
- * @param accountId an app-specified identifier for the account
*
+ * @param deviceId an app-specified identifier for the device
+ * @param accountId an app-specified identifier for the account
* @return true if the entry was removed, false otherwise.
*/
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
@@ -608,6 +595,7 @@ public final class E2eeContactKeysManager {
/**
* Possible values of verification state.
+ *
* @hide
*/
@IntDef(prefix = {"VERIFICATION_STATE_"}, value = {
@@ -616,7 +604,8 @@ public final class E2eeContactKeysManager {
VERIFICATION_STATE_VERIFIED
})
@Retention(RetentionPolicy.SOURCE)
- public @interface VerificationState {}
+ public @interface VerificationState {
+ }
/**
* Unverified state of a contact end to end encrypted key.
@@ -634,7 +623,8 @@ public final class E2eeContactKeysManager {
/** @hide */
public static final class E2eeContactKeys {
- private E2eeContactKeys() {}
+ private E2eeContactKeys() {
+ }
/**
* <p>
@@ -808,34 +798,7 @@ public final class E2eeContactKeysManager {
/**
* A parcelable class encapsulating other users' end to end encrypted contact key.
*/
- public static final class E2eeContactKey implements Parcelable {
- /**
- * An app-specified identifier for the device for which the end-to-end encryption
- * contact key can be used.
- */
- private final String mDeviceId;
-
- /**
- * An app-specified identifier for the account for which the end-to-end encryption
- * contact key can be used.
- * Usually a phone number.
- */
- private final String mAccountId;
-
- /**
- * Owner application package name.
- */
- private final String mOwnerPackageName;
-
- /**
- * Timestamp at which the key was updated.
- */
- private final long mTimeUpdated;
-
- /**
- * The raw bytes for the key.
- */
- private final byte[] mKeyValue;
+ public static final class E2eeContactKey extends E2eeBaseKey implements Parcelable {
/**
* Describes the local verification state for the key, for instance QR-code based
@@ -844,12 +807,6 @@ public final class E2eeContactKeysManager {
private final int mLocalVerificationState;
/**
- * Describes the remote verification state for the key, for instance through a key
- * transparency server.
- */
- private final int mRemoteVerificationState;
-
- /**
* The display name for the contact.
*/
private final String mDisplayName;
@@ -873,77 +830,15 @@ public final class E2eeContactKeysManager {
@VerificationState int remoteVerificationState,
@Nullable String displayName,
@Nullable String phoneNumber, @Nullable String emailAddress) {
- this.mDeviceId = deviceId;
- this.mAccountId = accountId;
- this.mOwnerPackageName = ownerPackageName;
- this.mTimeUpdated = timeUpdated;
- this.mKeyValue = keyValue == null ? null : Arrays.copyOf(keyValue, keyValue.length);
+ super(deviceId, accountId, ownerPackageName, timeUpdated, keyValue,
+ remoteVerificationState);
this.mLocalVerificationState = localVerificationState;
- this.mRemoteVerificationState = remoteVerificationState;
this.mDisplayName = displayName;
this.mPhoneNumber = phoneNumber;
this.mEmailAddress = emailAddress;
}
/**
- * Gets the app-specified identifier for the device for which the end-to-end encryption
- * contact key can be used.
- * Returns null if the app doesn't have the required visibility into
- * the end-to-end encryption contact key.
- *
- * @return An app-specified identifier for the device.
- */
- @Nullable
- public String getDeviceId() {
- return mDeviceId;
- }
-
- /**
- * Gets the app-specified identifier for the account for which the end-to-end encryption
- * contact key can be used.
- * Usually a phone number.
- *
- * @return An app-specified identifier for the account.
- */
- @NonNull
- public String getAccountId() {
- return mAccountId;
- }
-
- /**
- * Gets the owner application package name.
- *
- * @return The owner application package name.
- */
- @NonNull
- public String getOwnerPackageName() {
- return mOwnerPackageName;
- }
-
- /**
- * Gets the timestamp at which the key was updated. Returns -1 if the app doesn't have the
- * required visibility into the end-to-end encryption contact key.
- *
- * @return The timestamp at which the key was updated in the System.currentTimeMillis()
- * base.
- */
- public long getTimeUpdated() {
- return mTimeUpdated;
- }
-
- /**
- * Gets the raw bytes for the key.
- * Returns null if the app doesn't have the required visibility into
- * the end-to-end encryption contact key.
- *
- * @return A copy of the raw bytes for the key.
- */
- @Nullable
- public byte[] getKeyValue() {
- return mKeyValue == null ? null : Arrays.copyOf(mKeyValue, mKeyValue.length);
- }
-
- /**
* Gets the local verification state for the key, for instance QR-code based verification.
*
* @return The local verification state for the key.
@@ -953,16 +848,6 @@ public final class E2eeContactKeysManager {
}
/**
- * Gets the remote verification state for the key, for instance through a key transparency
- * server.
- *
- * @return The remote verification state for the key.
- */
- public @VerificationState int getRemoteVerificationState() {
- return mRemoteVerificationState;
- }
-
- /**
* Gets the display name for the contact.
*
* @return The display name for the contact.
@@ -1079,46 +964,126 @@ public final class E2eeContactKeysManager {
/**
* A parcelable class encapsulating self end to end encrypted contact key.
*/
- public static final class E2eeSelfKey implements Parcelable {
+ public static final class E2eeSelfKey extends E2eeBaseKey implements Parcelable {
/**
- * An app-specified identifier for the device for which the end-to-end encryption
- * contact key can be used.
+ * @hide
+ */
+ public E2eeSelfKey(@Nullable String deviceId, @NonNull String accountId,
+ @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue,
+ @VerificationState int remoteVerificationState) {
+ super(deviceId, accountId, ownerPackageName, timeUpdated, keyValue,
+ remoteVerificationState);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated,
+ Arrays.hashCode(mKeyValue), mRemoteVerificationState);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (obj == this) return true;
+
+ if (!(obj instanceof E2eeSelfKey toCompare)) {
+ return false;
+ }
+
+ return Objects.equals(mDeviceId, toCompare.mDeviceId)
+ && Objects.equals(mAccountId, toCompare.mAccountId)
+ && Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName)
+ && mTimeUpdated == toCompare.mTimeUpdated
+ && Arrays.equals(mKeyValue, toCompare.mKeyValue)
+ && mRemoteVerificationState == toCompare.mRemoteVerificationState;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mDeviceId);
+ dest.writeString8(mAccountId);
+ dest.writeString8(mOwnerPackageName);
+ dest.writeLong(mTimeUpdated);
+ dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL);
+ if (mKeyValue != null) {
+ dest.writeByteArray(mKeyValue);
+ }
+ dest.writeInt(mRemoteVerificationState);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<E2eeSelfKey> CREATOR =
+ new Creator<>() {
+ @Override
+ public E2eeSelfKey createFromParcel(Parcel source) {
+ String deviceId = source.readString8();
+ String accountId = source.readString8();
+ String ownerPackageName = source.readString8();
+ long timeUpdated = source.readLong();
+ int keyValueLength = source.readInt();
+ byte[] keyValue;
+ if (keyValueLength > 0) {
+ keyValue = new byte[keyValueLength];
+ source.readByteArray(keyValue);
+ } else {
+ keyValue = null;
+ }
+ int remoteVerificationState = source.readInt();
+ return new E2eeSelfKey(deviceId, accountId, ownerPackageName,
+ timeUpdated, keyValue, remoteVerificationState);
+ }
+
+ @Override
+ public E2eeSelfKey[] newArray(int size) {
+ return new E2eeSelfKey[size];
+ }
+ };
+ }
+
+ /**
+ * An abstract class that's extended by self and contact key classes.
+ *
+ * @hide
+ */
+ abstract static class E2eeBaseKey {
+ /**
+ * An app-specified identifier for the device for which the key can be used.
*/
- private final String mDeviceId;
+ protected final String mDeviceId;
/**
- * An app-specified identifier for the account for which the end-to-end encryption
- * contact key can be used.
+ * An app-specified identifier for the account for which the key can be used.
* Usually a phone number.
*/
- private final String mAccountId;
+ protected final String mAccountId;
/**
* Owner application package name.
*/
- private final String mOwnerPackageName;
+ protected final String mOwnerPackageName;
/**
* Timestamp at which the key was updated.
*/
- private final long mTimeUpdated;
+ protected final long mTimeUpdated;
/**
* The raw bytes for the key.
*/
- private final byte[] mKeyValue;
-
+ protected final byte[] mKeyValue;
/**
- * Describes the remote verification state for the key, for instance through a key
- * transparency server.
+ * Describes the remote verification state for the end-to-end encryption key, for instance
+ * through a key transparency server.
*/
- private final int mRemoteVerificationState;
+ protected final int mRemoteVerificationState;
- /**
- * @hide
- */
- public E2eeSelfKey(@Nullable String deviceId, @NonNull String accountId,
+ protected E2eeBaseKey(@Nullable String deviceId, @NonNull String accountId,
@NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue,
@VerificationState int remoteVerificationState) {
this.mDeviceId = deviceId;
@@ -1131,9 +1096,9 @@ public final class E2eeContactKeysManager {
/**
* Gets the app-specified identifier for the device for which the end-to-end encryption
- * contact key can be used.
+ * key can be used.
* Returns null if the app doesn't have the required visibility into
- * the end-to-end encryption contact key.
+ * the end-to-end encryption key.
*
* @return An app-specified identifier for the device.
*/
@@ -1144,10 +1109,10 @@ public final class E2eeContactKeysManager {
/**
* Gets the app-specified identifier for the account for which the end-to-end encryption
- * contact key can be used.
+ * key can be used.
* Usually a phone number.
*
- * @return An app-specified identifier for the device.
+ * @return An app-specified identifier for the account.
*/
@NonNull
public String getAccountId() {
@@ -1165,8 +1130,8 @@ public final class E2eeContactKeysManager {
}
/**
- * Gets the timestamp at which the key was updated. Returns -1 if the app doesn't have the
- * required visibility into the end-to-end encryption contact key.
+ * Gets the timestamp at which the end-to-end encryption key was updated. Returns -1 if
+ * the app doesn't have the required visibility into the key.
*
* @return The timestamp at which the key was updated in the System.currentTimeMillis()
* base.
@@ -1176,11 +1141,11 @@ public final class E2eeContactKeysManager {
}
/**
- * Gets the raw bytes for the key.
+ * Gets the raw bytes for the end-to-end encryption key.
* Returns null if the app doesn't have the required visibility into
- * the end-to-end encryption contact key.
+ * the end-to-end encryption key.
*
- * @return A copy of the raw bytes for the key.
+ * @return A copy of the raw bytes for the end-to-end encryption key.
*/
@Nullable
public byte[] getKeyValue() {
@@ -1188,82 +1153,13 @@ public final class E2eeContactKeysManager {
}
/**
- * Gets the remote verification state for the key, for instance through a key transparency
- * server.
+ * Gets the remote verification state for the end-to-end encryption key, for instance
+ * through a key transparency server.
*
- * @return The remote verification state for the key.
+ * @return The remote verification state for the end-to-end encryption key.
*/
public @VerificationState int getRemoteVerificationState() {
return mRemoteVerificationState;
}
-
- @Override
- public int hashCode() {
- return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated,
- Arrays.hashCode(mKeyValue), mRemoteVerificationState);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) return false;
- if (obj == this) return true;
-
- if (!(obj instanceof E2eeSelfKey toCompare)) {
- return false;
- }
-
- return Objects.equals(mDeviceId, toCompare.mDeviceId)
- && Objects.equals(mAccountId, toCompare.mAccountId)
- && Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName)
- && mTimeUpdated == toCompare.mTimeUpdated
- && Arrays.equals(mKeyValue, toCompare.mKeyValue)
- && mRemoteVerificationState == toCompare.mRemoteVerificationState;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString8(mDeviceId);
- dest.writeString8(mAccountId);
- dest.writeString8(mOwnerPackageName);
- dest.writeLong(mTimeUpdated);
- dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL);
- if (mKeyValue != null) {
- dest.writeByteArray(mKeyValue);
- }
- dest.writeInt(mRemoteVerificationState);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- public static final Creator<E2eeSelfKey> CREATOR =
- new Creator<>() {
- @Override
- public E2eeSelfKey createFromParcel(Parcel source) {
- String deviceId = source.readString8();
- String accountId = source.readString8();
- String ownerPackageName = source.readString8();
- long timeUpdated = source.readLong();
- int keyValueLength = source.readInt();
- byte[] keyValue;
- if (keyValueLength > 0) {
- keyValue = new byte[keyValueLength];
- source.readByteArray(keyValue);
- } else {
- keyValue = null;
- }
- int remoteVerificationState = source.readInt();
- return new E2eeSelfKey(deviceId, accountId, ownerPackageName,
- timeUpdated, keyValue, remoteVerificationState);
- }
-
- @Override
- public E2eeSelfKey[] newArray(int size) {
- return new E2eeSelfKey[size];
- }
- };
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 20b4857bdd4d..eea6464c9047 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -471,6 +471,21 @@ public final class Settings {
"android.settings.ACCESSIBILITY_COLOR_MOTION_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of accessibility color contrast.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_ACCESSIBILITY_COLOR_CONTRAST_SETTINGS =
+ "android.settings.ACCESSIBILITY_COLOR_CONTRAST_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of Reduce Bright Colors.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -11561,6 +11576,15 @@ public final class Settings {
"extra_low_power_warning_acknowledged";
/**
+ * Whether the emergency thermal alert would be disabled
+ * (0: default) or not (1).
+ *
+ * @hide
+ */
+ public static final String EMERGENCY_THERMAL_ALERT_DISABLED =
+ "emergency_thermal_alert_disabled";
+
+ /**
* 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been
* suppressed.
* @hide
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index d8c44adcc322..f626149b03c4 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -92,7 +92,6 @@ public class ConfirmationPrompt {
private Executor mExecutor;
private Context mContext;
- private final KeyStore mKeyStore = KeyStore.getInstance();
private AndroidProtectedConfirmation mProtectedConfirmation;
private AndroidProtectedConfirmation getService() {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index f1054ec8ef15..c171c1b4b3b6 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -26,7 +26,6 @@ import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore2.AndroidKeyStoreProvider;
@@ -272,11 +271,9 @@ public class RecoveryController {
public static final int ERROR_KEY_NOT_FOUND = 30;
private final ILockSettings mBinder;
- private final KeyStore mKeyStore;
- private RecoveryController(ILockSettings binder, KeyStore keystore) {
+ private RecoveryController(ILockSettings binder) {
mBinder = binder;
- mKeyStore = keystore;
}
/**
@@ -296,7 +293,7 @@ public class RecoveryController {
// lockSettings may be null.
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryController(lockSettings, KeyStore.getInstance());
+ return new RecoveryController(lockSettings);
}
/**
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7658af53a7f8..bd9ab86fa8d1 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.UiThread;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
@@ -468,6 +469,7 @@ public abstract class NotificationListenerService extends Service {
* object as well as its identifying information (tag and id) and source
* (package name).
*/
+ @UiThread
public void onNotificationPosted(StatusBarNotification sbn) {
// optional
}
@@ -481,6 +483,7 @@ public abstract class NotificationListenerService extends Service {
* @param rankingMap The current ranking map that can be used to retrieve ranking information
* for active notifications, including the newly posted one.
*/
+ @UiThread
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
onNotificationPosted(sbn);
}
@@ -499,6 +502,7 @@ public abstract class NotificationListenerService extends Service {
* and source (package name) used to post the {@link android.app.Notification} that
* was just removed.
*/
+ @UiThread
public void onNotificationRemoved(StatusBarNotification sbn) {
// optional
}
@@ -520,6 +524,7 @@ public abstract class NotificationListenerService extends Service {
* for active notifications.
*
*/
+ @UiThread
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
onNotificationRemoved(sbn);
}
@@ -541,6 +546,7 @@ public abstract class NotificationListenerService extends Service {
* @param rankingMap The current ranking map that can be used to retrieve ranking information
* for active notifications.
*/
+ @UiThread
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
@NotificationCancelReason int reason) {
onNotificationRemoved(sbn, rankingMap);
@@ -552,6 +558,7 @@ public abstract class NotificationListenerService extends Service {
*
* @hide
*/
+ @UiThread
@SystemApi
public void onNotificationRemoved(@NonNull StatusBarNotification sbn,
@NonNull RankingMap rankingMap, @NonNull NotificationStats stats, int reason) {
@@ -563,6 +570,7 @@ public abstract class NotificationListenerService extends Service {
* the notification manager. You are safe to call {@link #getActiveNotifications()}
* at this time.
*/
+ @UiThread
public void onListenerConnected() {
// optional
}
@@ -572,6 +580,7 @@ public abstract class NotificationListenerService extends Service {
* notification manager.You will not receive any events after this call, and may only
* call {@link #requestRebind(ComponentName)} at this time.
*/
+ @UiThread
public void onListenerDisconnected() {
// optional
}
@@ -582,6 +591,7 @@ public abstract class NotificationListenerService extends Service {
* @param rankingMap The current ranking map that can be used to retrieve ranking information
* for active notifications.
*/
+ @UiThread
public void onNotificationRankingUpdate(RankingMap rankingMap) {
// optional
}
@@ -592,6 +602,7 @@ public abstract class NotificationListenerService extends Service {
*
* @param hints The current {@link #getCurrentListenerHints() listener hints}.
*/
+ @UiThread
public void onListenerHintsChanged(int hints) {
// optional
}
@@ -603,6 +614,7 @@ public abstract class NotificationListenerService extends Service {
* @param hideSilentStatusIcons whether or not status bar icons should be hidden for silent
* notifications
*/
+ @UiThread
public void onSilentStatusBarIconsVisibilityChanged(boolean hideSilentStatusIcons) {
// optional
}
@@ -620,6 +632,7 @@ public abstract class NotificationListenerService extends Service {
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
*/
+ @UiThread
public void onNotificationChannelModified(String pkg, UserHandle user,
NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) {
// optional
@@ -638,6 +651,7 @@ public abstract class NotificationListenerService extends Service {
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
*/
+ @UiThread
public void onNotificationChannelGroupModified(String pkg, UserHandle user,
NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) {
// optional
@@ -650,6 +664,7 @@ public abstract class NotificationListenerService extends Service {
* @param interruptionFilter The current
* {@link #getCurrentInterruptionFilter() interruption filter}.
*/
+ @UiThread
public void onInterruptionFilterChanged(int interruptionFilter) {
// optional
}
@@ -1197,6 +1212,11 @@ public abstract class NotificationListenerService extends Service {
* <p>
* Listen for updates using {@link #onInterruptionFilterChanged(int)}.
*
+ * <p>Apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above (with some
+ * exceptions, such as companion device managers) cannot modify the global interruption filter.
+ * Calling this method will instead activate or deactivate an
+ * {@link android.app.AutomaticZenRule} associated to the app.
+ *
* <p>The service should wait for the {@link #onListenerConnected()} event
* before performing this operation.
*
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 264b53c6ee40..d8210742e331 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -174,6 +174,23 @@ public class StatusBarNotification implements Parcelable {
return sbnKey;
}
+ /**
+ * @return Whether the Entry is a group child by the app or system
+ * @hide
+ */
+ public boolean isAppOrSystemGroupChild() {
+ return isGroup() && !getNotification().isGroupSummary();
+ }
+
+
+ /**
+ * @return Whether the Entry is a group summary by the app or system
+ * @hide
+ */
+ public boolean isAppOrSystemGroupSummary() {
+ return isGroup() && getNotification().isGroupSummary();
+ }
+
private String groupKey() {
if (overrideGroupKey != null) {
return user.getIdentifier() + "|" + pkg + "|" + "g:" + overrideGroupKey;
diff --git a/services/core/java/com/android/server/notification/ZenAdapters.java b/core/java/android/service/notification/ZenAdapters.java
index 37b263c3e3bd..b249815ce8db 100644
--- a/services/core/java/com/android/server/notification/ZenAdapters.java
+++ b/core/java/android/service/notification/ZenAdapters.java
@@ -14,25 +14,28 @@
* limitations under the License.
*/
-package com.android.server.notification;
+package android.service.notification;
+import android.annotation.NonNull;
import android.app.Flags;
import android.app.NotificationManager.Policy;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenPolicy;
/**
* Converters between different Zen representations.
+ * @hide
*/
-class ZenAdapters {
+public class ZenAdapters {
- static ZenPolicy notificationPolicyToZenPolicy(Policy policy) {
+ /** Maps {@link Policy} to {@link ZenPolicy}. */
+ @NonNull
+ public static ZenPolicy notificationPolicyToZenPolicy(@NonNull Policy policy) {
ZenPolicy.Builder zenPolicyBuilder = new ZenPolicy.Builder()
.allowAlarms(policy.allowAlarms())
.allowCalls(
policy.allowCalls()
- ? ZenModeConfig.getZenPolicySenders(policy.allowCallsFrom())
- : ZenPolicy.PEOPLE_TYPE_NONE)
+ ? notificationPolicySendersToZenPolicyPeopleType(
+ policy.allowCallsFrom())
+ : ZenPolicy.PEOPLE_TYPE_NONE)
.allowConversations(
policy.allowConversations()
? notificationPolicyConversationSendersToZenPolicy(
@@ -42,7 +45,8 @@ class ZenAdapters {
.allowMedia(policy.allowMedia())
.allowMessages(
policy.allowMessages()
- ? ZenModeConfig.getZenPolicySenders(policy.allowMessagesFrom())
+ ? notificationPolicySendersToZenPolicyPeopleType(
+ policy.allowMessagesFrom())
: ZenPolicy.PEOPLE_TYPE_NONE)
.allowReminders(policy.allowReminders())
.allowRepeatCallers(policy.allowRepeatCallers())
@@ -65,9 +69,58 @@ class ZenAdapters {
return zenPolicyBuilder.build();
}
+ /** Maps {@link ZenPolicy.PeopleType} enum to {@link Policy.PrioritySenders}. */
+ @Policy.PrioritySenders
+ public static int zenPolicyPeopleTypeToNotificationPolicySenders(
+ @ZenPolicy.PeopleType int zpPeopleType, @Policy.PrioritySenders int defaultResult) {
+ switch (zpPeopleType) {
+ case ZenPolicy.PEOPLE_TYPE_ANYONE:
+ return Policy.PRIORITY_SENDERS_ANY;
+ case ZenPolicy.PEOPLE_TYPE_CONTACTS:
+ return Policy.PRIORITY_SENDERS_CONTACTS;
+ case ZenPolicy.PEOPLE_TYPE_STARRED:
+ return Policy.PRIORITY_SENDERS_STARRED;
+ default:
+ return defaultResult;
+ }
+ }
+
+ /** Maps {@link Policy.PrioritySenders} enum to {@link ZenPolicy.PeopleType}. */
+ @ZenPolicy.PeopleType
+ public static int notificationPolicySendersToZenPolicyPeopleType(
+ @Policy.PrioritySenders int npPrioritySenders) {
+ switch (npPrioritySenders) {
+ case Policy.PRIORITY_SENDERS_ANY:
+ return ZenPolicy.PEOPLE_TYPE_ANYONE;
+ case Policy.PRIORITY_SENDERS_CONTACTS:
+ return ZenPolicy.PEOPLE_TYPE_CONTACTS;
+ case Policy.PRIORITY_SENDERS_STARRED:
+ default:
+ return ZenPolicy.PEOPLE_TYPE_STARRED;
+ }
+ }
+
+ /** Maps {@link ZenPolicy.ConversationSenders} enum to {@link Policy.ConversationSenders}. */
+ @Policy.ConversationSenders
+ public static int zenPolicyConversationSendersToNotificationPolicy(
+ @ZenPolicy.ConversationSenders int zpConversationSenders,
+ @Policy.ConversationSenders int defaultResult) {
+ switch (zpConversationSenders) {
+ case ZenPolicy.CONVERSATION_SENDERS_ANYONE:
+ return Policy.CONVERSATION_SENDERS_ANYONE;
+ case ZenPolicy.CONVERSATION_SENDERS_IMPORTANT:
+ return Policy.CONVERSATION_SENDERS_IMPORTANT;
+ case ZenPolicy.CONVERSATION_SENDERS_NONE:
+ return Policy.CONVERSATION_SENDERS_NONE;
+ default:
+ return defaultResult;
+ }
+ }
+
+ /** Maps {@link Policy.ConversationSenders} enum to {@link ZenPolicy.ConversationSenders}. */
@ZenPolicy.ConversationSenders
private static int notificationPolicyConversationSendersToZenPolicy(
- int npPriorityConversationSenders) {
+ @Policy.ConversationSenders int npPriorityConversationSenders) {
switch (npPriorityConversationSenders) {
case Policy.CONVERSATION_SENDERS_ANYONE:
return ZenPolicy.CONVERSATION_SENDERS_ANYONE;
@@ -75,8 +128,7 @@ class ZenAdapters {
return ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
case Policy.CONVERSATION_SENDERS_NONE:
return ZenPolicy.CONVERSATION_SENDERS_NONE;
- case Policy.CONVERSATION_SENDERS_UNSET:
- default:
+ default: // including Policy.CONVERSATION_SENDERS_UNSET
return ZenPolicy.CONVERSATION_SENDERS_UNSET;
}
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d9ca935b35b3..1d6dd1ebd54a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -24,6 +24,9 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+import static android.service.notification.ZenAdapters.notificationPolicySendersToZenPolicyPeopleType;
+import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy;
+import static android.service.notification.ZenAdapters.zenPolicyPeopleTypeToNotificationPolicySenders;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -1269,11 +1272,11 @@ public class ZenModeConfig implements Parcelable {
public ZenPolicy toZenPolicy() {
ZenPolicy.Builder builder = new ZenPolicy.Builder()
.allowCalls(allowCalls
- ? ZenModeConfig.getZenPolicySenders(allowCallsFrom)
+ ? notificationPolicySendersToZenPolicyPeopleType(allowCallsFrom)
: ZenPolicy.PEOPLE_TYPE_NONE)
.allowRepeatCallers(allowRepeatCallers)
.allowMessages(allowMessages
- ? ZenModeConfig.getZenPolicySenders(allowMessagesFrom)
+ ? notificationPolicySendersToZenPolicyPeopleType(allowMessagesFrom)
: ZenPolicy.PEOPLE_TYPE_NONE)
.allowReminders(allowReminders)
.allowEvents(allowEvents)
@@ -1333,14 +1336,14 @@ public class ZenModeConfig implements Parcelable {
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
- messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityMessageSenders(),
- messageSenders);
+ messageSenders = zenPolicyPeopleTypeToNotificationPolicySenders(
+ zenPolicy.getPriorityMessageSenders(), messageSenders);
}
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
- conversationSenders = getConversationSendersWithDefault(
+ conversationSenders = zenPolicyConversationSendersToNotificationPolicy(
zenPolicy.getPriorityConversationSenders(), conversationSenders);
} else {
conversationSenders = CONVERSATION_SENDERS_NONE;
@@ -1349,8 +1352,8 @@ public class ZenModeConfig implements Parcelable {
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
- callSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders(),
- callSenders);
+ callSenders = zenPolicyPeopleTypeToNotificationPolicySenders(
+ zenPolicy.getPriorityCallSenders(), callSenders);
}
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS,
@@ -1449,47 +1452,6 @@ public class ZenModeConfig implements Parcelable {
return (policy.suppressedVisualEffects & visualEffect) == 0;
}
- private static int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
- int defaultPolicySender) {
- switch (senders) {
- case ZenPolicy.PEOPLE_TYPE_ANYONE:
- return Policy.PRIORITY_SENDERS_ANY;
- case ZenPolicy.PEOPLE_TYPE_CONTACTS:
- return Policy.PRIORITY_SENDERS_CONTACTS;
- case ZenPolicy.PEOPLE_TYPE_STARRED:
- return Policy.PRIORITY_SENDERS_STARRED;
- default:
- return defaultPolicySender;
- }
- }
-
- private static int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
- int defaultPolicySender) {
- switch (senders) {
- case ZenPolicy.CONVERSATION_SENDERS_ANYONE:
- case ZenPolicy.CONVERSATION_SENDERS_IMPORTANT:
- case ZenPolicy.CONVERSATION_SENDERS_NONE:
- return senders;
- default:
- return defaultPolicySender;
- }
- }
-
- /**
- * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType
- */
- public static @ZenPolicy.PeopleType int getZenPolicySenders(int senders) {
- switch (senders) {
- case Policy.PRIORITY_SENDERS_ANY:
- return ZenPolicy.PEOPLE_TYPE_ANYONE;
- case Policy.PRIORITY_SENDERS_CONTACTS:
- return ZenPolicy.PEOPLE_TYPE_CONTACTS;
- case Policy.PRIORITY_SENDERS_STARRED:
- default:
- return ZenPolicy.PEOPLE_TYPE_STARRED;
- }
- }
-
public Policy toNotificationPolicy() {
int priorityCategories = 0;
int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
@@ -1524,7 +1486,7 @@ public class ZenModeConfig implements Parcelable {
}
priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
- priorityConversationSenders = getConversationSendersWithDefault(
+ priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
allowConversationsFrom, priorityConversationSenders);
int state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0;
@@ -1559,15 +1521,6 @@ public class ZenModeConfig implements Parcelable {
}
}
- private static int prioritySendersToSource(int prioritySenders, int def) {
- switch (prioritySenders) {
- case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
- case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
- case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
- default: return def;
- }
- }
-
private static int normalizePrioritySenders(int prioritySenders, int def) {
if (!(prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS
|| prioritySenders == Policy.PRIORITY_SENDERS_STARRED
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
index bbb4bc6c0272..e44c69c4df28 100644
--- a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
@@ -26,6 +26,7 @@ import android.app.ondeviceintelligence.IFeatureCallback;
import android.app.ondeviceintelligence.IListFeaturesCallback;
import android.app.ondeviceintelligence.IFeatureDetailsCallback;
import com.android.internal.infra.AndroidFuture;
+import android.service.ondeviceintelligence.IRemoteProcessingService;
/**
@@ -41,4 +42,5 @@ oneway interface IOnDeviceIntelligenceService {
void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future);
void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
void requestFeatureDownload(in Feature feature, in ICancellationSignal cancellationSignal, in IDownloadCallback downloadCallback);
+ void registerRemoteServices(in IRemoteProcessingService remoteProcessingService);
} \ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
index 08eb9278fcc4..e3fda04e6592 100644
--- a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
@@ -23,8 +23,10 @@ import android.app.ondeviceintelligence.IProcessingSignal;
import android.app.ondeviceintelligence.Content;
import android.app.ondeviceintelligence.Feature;
import android.os.ICancellationSignal;
+import android.os.PersistableBundle;
+import android.os.Bundle;
import android.service.ondeviceintelligence.IRemoteStorageService;
-
+import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
/**
* Interface for a concrete implementation to provide on device trusted inference.
@@ -41,4 +43,6 @@ oneway interface IOnDeviceTrustedInferenceService {
void processRequestStreaming(in Feature feature, in Content request, in int requestType,
in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal,
in IStreamingResponseCallback callback);
+ void updateProcessingState(in Bundle processingState,
+ in IProcessingUpdateStatusCallback callback);
} \ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl b/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
new file mode 100644
index 000000000000..7ead8690abb4
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IProcessingUpdateStatusCallback.aidl
@@ -0,0 +1,14 @@
+package android.service.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving status from a updateProcessingState call from on-device intelligence
+ * service.
+ *
+ * @hide
+ */
+interface IProcessingUpdateStatusCallback {
+ void onSuccess(in PersistableBundle statusParams) = 1;
+ void onFailure(int errorCode, in String errorMessage) = 2;
+}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
index fc3912e2aa52..32a8a6a70406 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/core/java/android/service/ondeviceintelligence/IRemoteProcessingService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.scene
+package android.service.ondeviceintelligence;
-import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
-import dagger.Module
-import dagger.Provides
+import android.os.Bundle;
+import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
-@Module
-interface LockscreenSceneModule {
- companion object {
- @Provides
- fun providesLockscreenBlueprints(): Set<LockscreenSceneBlueprint> {
- return emptySet()
- }
- }
+
+/**
+ * Interface for a concrete implementation to provide methods to update state of a remote service.
+ *
+ * @hide
+ */
+oneway interface IRemoteProcessingService {
+ void updateProcessingState(in Bundle processingState,
+ in IProcessingUpdateStatusCallback callback);
}
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
index 0cba1d37721a..46ba25d40bec 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -18,6 +18,7 @@ package android.service.ondeviceintelligence;
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,11 +48,18 @@ import android.util.Slog;
import com.android.internal.infra.AndroidFuture;
+import androidx.annotation.IntDef;
+
import java.io.File;
import java.io.FileNotFoundException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
@@ -64,6 +72,10 @@ import java.util.function.LongConsumer;
* {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is
* returned.
*
+ * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are
+ * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended
+ * that implementations of this system-service expose this API to the clients via a library which
+ * has more defined contract.</p>
* <pre>
* {@literal
* <service android:name=".SampleOnDeviceIntelligenceService"
@@ -78,6 +90,8 @@ import java.util.function.LongConsumer;
public abstract class OnDeviceIntelligenceService extends Service {
private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
+ private volatile IRemoteProcessingService mRemoteProcessingService;
+
/**
* The {@link Intent} that must be declared as handled by the service. To be supported, the
* service must also require the
@@ -88,6 +102,7 @@ public abstract class OnDeviceIntelligenceService extends Service {
public static final String SERVICE_INTERFACE =
"android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+
/**
* @hide
*/
@@ -167,12 +182,58 @@ public abstract class OnDeviceIntelligenceService extends Service {
remoteCallback.sendResult(bundle);
});
}
+
+ @Override
+ public void registerRemoteServices(
+ IRemoteProcessingService remoteProcessingService) {
+ mRemoteProcessingService = remoteProcessingService;
+ }
};
}
Slog.w(TAG, "Incorrect service interface, returning null.");
return null;
}
+ /**
+ * Invoked by the {@link OnDeviceIntelligenceService} inorder to send updates to the inference
+ * service if there is a state change to be performed.
+ *
+ * @param processingState the updated state to be applied.
+ * @param callbackExecutor executor to the run status callback on.
+ * @param statusReceiver receiver to get status of the update state operation.
+ */
+ public final void updateProcessingState(@NonNull Bundle processingState,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<PersistableBundle, OnDeviceUpdateProcessingException> statusReceiver) {
+ Objects.requireNonNull(callbackExecutor);
+ if (mRemoteProcessingService == null) {
+ throw new IllegalStateException("Remote processing service is unavailable.");
+ }
+ try {
+ mRemoteProcessingService.updateProcessingState(processingState,
+ new IProcessingUpdateStatusCallback.Stub() {
+ @Override
+ public void onSuccess(PersistableBundle result) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> statusReceiver.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> statusReceiver.onError(
+ new OnDeviceUpdateProcessingException(
+ errorCode, errorMessage))));
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error in updateProcessingState: " + e);
+ throw new RuntimeException(e);
+ }
+ }
+
private OutcomeReceiver<Feature,
OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureCallback(
IFeatureCallback featureCallback) {
@@ -197,7 +258,6 @@ public abstract class OnDeviceIntelligenceService extends Service {
}
}
};
-
}
private OutcomeReceiver<List<Feature>,
@@ -380,4 +440,60 @@ public abstract class OnDeviceIntelligenceService extends Service {
* @param versionConsumer consumer to populate the version.
*/
public abstract void onGetVersion(@NonNull LongConsumer versionConsumer);
+
+
+ /**
+ * Exception type to be populated when calls to {@link #updateProcessingState} fail.
+ */
+ public static class OnDeviceUpdateProcessingException extends
+ OnDeviceIntelligenceServiceException {
+ /**
+ * The connection to remote service failed and the processing state could not be updated.
+ */
+ public static final int PROCESSING_UPDATE_STATUS_CONNECTION_FAILED = 1;
+
+
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ PROCESSING_UPDATE_STATUS_CONNECTION_FAILED
+ }, open = true)
+ @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER,
+ ElementType.FIELD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCode {
+ }
+
+ public OnDeviceUpdateProcessingException(@ErrorCode int errorCode) {
+ super(errorCode);
+ }
+
+ public OnDeviceUpdateProcessingException(@ErrorCode int errorCode,
+ @NonNull String errorMessage) {
+ super(errorCode, errorMessage);
+ }
+ }
+
+ /**
+ * Exception type to be used for surfacing errors to service implementation.
+ */
+ public abstract static class OnDeviceIntelligenceServiceException extends Exception {
+ private final int mErrorCode;
+
+ public OnDeviceIntelligenceServiceException(int errorCode) {
+ this.mErrorCode = errorCode;
+ }
+
+ public OnDeviceIntelligenceServiceException(int errorCode,
+ @NonNull String errorMessage) {
+ super(errorMessage);
+ this.mErrorCode = errorCode;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ }
}
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
index 96982e3d7829..86001975cc09 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
@@ -36,13 +36,16 @@ import android.app.ondeviceintelligence.ProcessingSignal;
import android.app.ondeviceintelligence.StreamingResponseReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException;
import android.util.Log;
import android.util.Slog;
@@ -65,6 +68,11 @@ import java.util.function.Consumer;
* non-streaming fashion. Also, provides a way to register a storage service that will be used to
* read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p>
*
+ * <p> Similar to {@link OnDeviceIntelligenceManager} class, the contracts in this service are
+ * defined to be open-ended in general, to allow interoperability. Therefore, it is recommended
+ * that implementations of this system-service expose this API to the clients via a library which
+ * has more defined contract.</p>
+ *
* <pre>
* {@literal
* <service android:name=".SampleTrustedInferenceService"
@@ -152,6 +160,17 @@ public abstract class OnDeviceTrustedInferenceService extends Service {
wrapResponseCallback(callback)
);
}
+
+ @Override
+ public void updateProcessingState(Bundle processingState,
+ IProcessingUpdateStatusCallback callback) {
+ Objects.requireNonNull(processingState);
+ Objects.requireNonNull(callback);
+
+ OnDeviceTrustedInferenceService.this.onUpdateProcessingState(processingState,
+ wrapOutcomeReceiver(callback)
+ );
+ }
};
}
Slog.w(TAG, "Incorrect service interface, returning null.");
@@ -233,6 +252,21 @@ public abstract class OnDeviceTrustedInferenceService extends Service {
@NonNull OutcomeReceiver<Content,
OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+ /**
+ * Invoked when processing environment needs to be updated or refreshed with fresh
+ * configuration, files or state.
+ *
+ * @param processingState contains updated state and params that are to be applied to the
+ * processing environmment,
+ * @param callback callback to populate the update status and if there are params
+ * associated with the status.
+ */
+ public abstract void onUpdateProcessingState(@NonNull Bundle processingState,
+ @NonNull OutcomeReceiver<PersistableBundle,
+ OnDeviceUpdateProcessingException> callback);
+
+
/**
* Overrides {@link Context#openFileInput} to read files with the given file names under the
* internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in
@@ -407,4 +441,31 @@ public abstract class OnDeviceTrustedInferenceService extends Service {
}
};
}
+
+ @NonNull
+ private static OutcomeReceiver<PersistableBundle, OnDeviceUpdateProcessingException> wrapOutcomeReceiver(
+ IProcessingUpdateStatusCallback callback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull PersistableBundle result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+
+ }
+ }
+
+ @Override
+ public void onError(
+ @androidx.annotation.NonNull OnDeviceUpdateProcessingException error) {
+ try {
+ callback.onFailure(error.getErrorCode(), error.getMessage());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending exception details: " + e);
+ }
+ }
+ };
+ }
+
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 20adc5427495..306410c9a98b 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -519,7 +519,7 @@ public class VoiceInteractionService extends Service {
@NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
- // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
+ // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
@@ -545,10 +545,6 @@ public class VoiceInteractionService extends Service {
@NonNull SoundTrigger.ModuleProperties moduleProperties,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
- // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
- // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale,
- // SoundTrigger.ModuleProperties, AlwaysOnHotwordDetector.Callback)} and replace with the
- // permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
@@ -615,11 +611,6 @@ public class VoiceInteractionService extends Service {
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory,
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
- // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
- // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
- // AlwaysOnHotwordDetector.Callback)} and replace with the permission
- // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
-
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ true, options, sharedMemory,
/* modulProperties */ null, /* executor= */ null, callback);
@@ -671,11 +662,7 @@ public class VoiceInteractionService extends Service {
@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
- // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
- // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
- // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
- // Executor, AlwaysOnHotwordDetector.Callback)} and replace with the permission
- // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
+ // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
@@ -702,10 +689,6 @@ public class VoiceInteractionService extends Service {
@NonNull SoundTrigger.ModuleProperties moduleProperties,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
- // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
- // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale, PersistableBundle,
- // SharedMemory, SoundTrigger.ModuleProperties, Executor, AlwaysOnHotwordDetector.Callback)}
- // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index b1bca96c5c09..cedba85f55dc 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -8,7 +8,7 @@ flag {
}
flag {
- name: "perfetto_protolog"
+ name: "perfetto_protolog_tracing"
namespace: "windowing_tools"
description: "Migrate protolog to Perfetto"
bug: "276432490"
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 00657ea35e02..66655fca8fc3 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -188,8 +188,7 @@ public class HandwritingInitiator {
// check whether the stylus we are tracking goes up.
if (mState != null) {
mState.mShouldInitHandwriting = false;
- if (!mState.mHasInitiatedHandwriting
- && !mState.mHasPreparedHandwritingDelegation) {
+ if (!mState.mHandled) {
// The user just did a click, long click or another stylus gesture,
// show hover icon again for the connected view.
mShowHoverIconForConnectedView = true;
@@ -204,16 +203,14 @@ public class HandwritingInitiator {
// Either we've already tried to initiate handwriting, or the ongoing MotionEvent
// sequence is considered to be tap, long-click or other gestures.
if (!mState.mShouldInitHandwriting || mState.mExceedHandwritingSlop) {
- return mState.mHasInitiatedHandwriting
- || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
final long timeElapsed =
motionEvent.getEventTime() - mState.mStylusDownTimeInMillis;
if (timeElapsed > mHandwritingTimeoutInMillis) {
mState.mShouldInitHandwriting = false;
- return mState.mHasInitiatedHandwriting
- || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
final int pointerIndex = motionEvent.findPointerIndex(mState.mStylusPointerId);
@@ -223,7 +220,7 @@ public class HandwritingInitiator {
mState.mExceedHandwritingSlop = true;
View candidateView = findBestCandidateView(mState.mStylusDownX,
mState.mStylusDownY, /* isHover */ false);
- if (candidateView != null) {
+ if (candidateView != null && candidateView.isEnabled()) {
if (candidateView == getConnectedOrFocusedView()) {
if (!mInitiateWithoutConnection && !candidateView.hasFocus()) {
requestFocusWithoutReveal(candidateView);
@@ -246,7 +243,7 @@ public class HandwritingInitiator {
}
}
}
- return mState.mHasInitiatedHandwriting || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
return false;
}
@@ -382,7 +379,7 @@ public class HandwritingInitiator {
@VisibleForTesting
public void startHandwriting(@NonNull View view) {
mImm.startStylusHandwriting(view);
- mState.mHasInitiatedHandwriting = true;
+ mState.mHandled = true;
mState.mShouldInitHandwriting = false;
mShowHoverIconForConnectedView = false;
if (view instanceof TextView) {
@@ -402,13 +399,12 @@ public class HandwritingInitiator {
mImm.startConnectionlessStylusHandwritingForDelegation(
view, getCursorAnchorInfoForConnectionless(view), delegatePackageName,
view::post, new DelegationCallback(view, delegatePackageName));
- mState.mHasInitiatedHandwriting = true;
mState.mShouldInitHandwriting = false;
} else {
mImm.prepareStylusHandwritingDelegation(view, delegatePackageName);
view.getHandwritingDelegatorCallback().run();
- mState.mHasPreparedHandwritingDelegation = true;
}
+ mState.mHandled = true;
}
/**
@@ -455,7 +451,7 @@ public class HandwritingInitiator {
private void onDelegationAccepted(View view) {
if (mState != null) {
- mState.mHasInitiatedHandwriting = true;
+ mState.mHandled = true;
mState.mShouldInitHandwriting = false;
}
if (view instanceof TextView) {
@@ -795,12 +791,12 @@ public class HandwritingInitiator {
* This boolean will be set to false, and it won't request to start handwriting.
*/
private boolean mShouldInitHandwriting;
+
/**
- * Whether handwriting mode has already been initiated for the current MotionEvent sequence.
+ * Whether the current MotionEvent sequence has been handled by the handwriting initiator,
+ * either by initiating handwriting mode, or by preparing handwriting delegation.
*/
- private boolean mHasInitiatedHandwriting;
-
- private boolean mHasPreparedHandwritingDelegation;
+ private boolean mHandled;
/**
* Whether the current ongoing stylus MotionEvent sequence already exceeds the
@@ -838,8 +834,7 @@ public class HandwritingInitiator {
mStylusDownY = motionEvent.getY(actionIndex);
mShouldInitHandwriting = true;
- mHasInitiatedHandwriting = false;
- mHasPreparedHandwritingDelegation = false;
+ mHandled = false;
mExceedHandwritingSlop = false;
}
}
@@ -1052,8 +1047,6 @@ public class HandwritingInitiator {
// Fall back to the old delegation flow
mImm.prepareStylusHandwritingDelegation(mView, mDelegatePackageName);
mView.getHandwritingDelegatorCallback().run();
- mState.mHasInitiatedHandwriting = false;
- mState.mHasPreparedHandwritingDelegation = true;
break;
}
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 11180aef4479..5ee526e0343d 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -73,7 +73,7 @@ oneway interface IWindow {
*
* @param types internal insets types (WindowInsets.Type.InsetsType) to show
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
void showInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
@@ -82,7 +82,7 @@ oneway interface IWindow {
*
* @param types internal insets types (WindowInsets.Type.InsetsType) to hide
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
void hideInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d68a47c54d4b..e126836020b4 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -148,13 +148,13 @@ interface IWindowSession {
int seqId);
@UnsupportedAppUsage
- boolean performHapticFeedback(int effectId, boolean always);
+ boolean performHapticFeedback(int effectId, boolean always, boolean fromIme);
/**
* Called by attached views to perform predefined haptic feedback without requiring VIBRATE
* permission.
*/
- oneway void performHapticFeedbackAsync(int effectId, boolean always);
+ oneway void performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme);
/**
* Initiate the drag operation itself
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index de809c8489fd..821e13d85370 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -21,9 +21,9 @@ import static android.view.ImeInsetsSourceConsumerProto.HAS_PENDING_REQUEST;
import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
-import android.os.Process;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
@@ -70,7 +70,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
if (!isShowRequested()) {
mIsRequestedVisibleAwaitingControl = false;
if (!running && !mHasPendingRequest) {
- notifyHidden(null /* statsToken */);
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED,
+ mController.getHost().isHandlingPointerEvent() /* fromUser */);
+ notifyHidden(statsToken);
removeSurface();
}
}
@@ -144,9 +148,17 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
if (!fromIme) {
+ // Create a new token to track the hide request when we have control,
+ // as we use the passed in token for the insets animation already.
+ final var notifyStatsToken = getControl() != null
+ ? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
+ mController.getHost().isHandlingPointerEvent() /* fromUser */)
+ : statsToken;
// The insets might be controlled by a remote target. Let the server know we are
// requested to hide.
- notifyHidden(statsToken);
+ notifyHidden(notifyStatsToken);
}
if (mAnimationState == ANIMATION_STATE_SHOW) {
mHasPendingRequest = true;
@@ -157,21 +169,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
* Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
* IME insets are hidden.
*
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
- private void notifyHidden(@Nullable ImeTracker.Token statsToken) {
- // Create a new stats token to track the hide request when:
- // - we do not already have one, or
- // - we do already have one, but we have control and use the passed in token
- // for the insets animation already.
- if (statsToken == null || getControl() != null) {
- statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
- Process.myUid(),
- ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
- mController.getHost().isHandlingPointerEvent() /* fromUser */);
- }
-
+ private void notifyHidden(@NonNull ImeTracker.Token statsToken) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 7f1e037e92d4..85c779bc8c79 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -43,7 +43,6 @@ import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
-import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -165,9 +164,9 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
mStatsToken = statsToken;
if (DEBUG_IME_VISIBILITY && (types & ime()) != 0) {
EventLog.writeEvent(IMF_IME_ANIM_START,
- mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType,
- mCurrentAlpha, "Current:" + mCurrentInsets, "Shown:" + mShownInsets,
- "Hidden:" + mHiddenInsets);
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mAnimationType, mCurrentAlpha, "Current:" + mCurrentInsets,
+ "Shown:" + mShownInsets, "Hidden:" + mHiddenInsets);
}
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets));
@@ -245,6 +244,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
}
@Override
+ @Nullable
public ImeTracker.Token getStatsToken() {
return mStatsToken;
}
@@ -330,8 +330,8 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
mListener.onFinished(this);
if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) {
EventLog.writeEvent(IMF_IME_ANIM_FINISH,
- mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType,
- mCurrentAlpha, shown ? 1 : 0, Objects.toString(insets));
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mAnimationType, mCurrentAlpha, shown ? 1 : 0, Objects.toString(insets));
}
}
@@ -355,8 +355,8 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
if (DEBUG) Log.d(TAG, "notify Control request cancelled for types: " + mTypes);
if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) {
EventLog.writeEvent(IMF_IME_ANIM_CANCEL,
- mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType,
- Objects.toString(mPendingInsets));
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mAnimationType, Objects.toString(mPendingInsets));
}
releaseLeashes();
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 079991a81e77..92e20e09d8c4 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -137,6 +137,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
}
@Override
+ @Nullable
public ImeTracker.Token getStatsToken() {
return mControl.getStatsToken();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 1803a6ed237f..6cc4b20dcde9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -28,7 +28,6 @@ import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.ime;
-import static android.view.inputmethod.ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL;
import android.animation.AnimationHandler;
import android.animation.Animator;
@@ -47,7 +46,6 @@ import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Process;
import android.os.Trace;
import android.text.TextUtils;
import android.util.IntArray;
@@ -659,6 +657,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private final Runnable mAnimCallback;
/** Pending control request that is waiting on IME to be ready to be shown */
+ @Nullable
private PendingControlRequest mPendingImeControlRequest;
private int mWindowType;
@@ -1043,12 +1042,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
hideTypes[0] &= ~animatingTypes;
if (showTypes[0] != 0) {
- applyAnimation(showTypes[0], true /* show */, false /* fromIme */,
- null /* statsToken */);
+ final var statsToken = (showTypes[0] & ime()) == 0 ? null
+ : ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken);
}
if (hideTypes[0] != 0) {
- applyAnimation(hideTypes[0], false /* show */, false /* fromIme */,
- null /* statsToken */);
+ final var statsToken = (hideTypes[0] & ime()) == 0 ? null
+ : ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken);
}
if (mControllableTypes != controllableTypes) {
@@ -1064,15 +1069,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void show(@InsetsType int types) {
- ImeTracker.Token statsToken = null;
- if ((types & ime()) != 0) {
- statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API,
- mHost.isHandlingPointerEvent() /* fromUser */);
- }
-
- show(types, false /* fromIme */, statsToken);
+ show(types, false /* fromIme */, null /* statsToken */);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -1080,6 +1077,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Nullable ImeTracker.Token statsToken) {
if ((types & ime()) != 0) {
Log.d(TAG, "show(ime(), fromIme=" + fromIme + ")");
+
+ if (statsToken == null) {
+ statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ }
}
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("InsetsController#show",
@@ -1148,9 +1152,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
/**
- * Handle the {@link #mPendingImeControlRequest} when
- * - The IME insets is ready to show.
- * - The IME insets has being requested invisible.
+ * Handle the {@link #mPendingImeControlRequest} when:
+ * <ul>
+ * <li> The IME insets is ready to show.
+ * <li> The IME insets has being requested invisible.
+ * </ul>
*/
private void handlePendingControlRequest(@Nullable ImeTracker.Token statsToken) {
PendingControlRequest pendingRequest = mPendingImeControlRequest;
@@ -1170,20 +1176,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void hide(@InsetsType int types) {
- ImeTracker.Token statsToken = null;
- if ((types & ime()) != 0) {
- statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
- mHost.isHandlingPointerEvent() /* fromUser */);
- }
-
- hide(types, false /* fromIme */, statsToken);
+ hide(types, false /* fromIme */, null /* statsToken */);
}
@VisibleForTesting
public void hide(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
+ if ((types & ime()) != 0) {
+ Log.d(TAG, "hide(ime(), fromIme=" + fromIme + ")");
+
+ if (statsToken == null) {
+ statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ }
+ }
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("InsetsController#hide",
mHost.getInputMethodManager(), null /* icProto */);
@@ -1307,10 +1315,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (monitoredAnimation && (types & Type.ime()) != 0) {
if (animationType == ANIMATION_TYPE_SHOW) {
ImeTracker.forLatency().onShowCancelled(statsToken,
- PHASE_CLIENT_ANIMATION_CANCEL, ActivityThread::currentApplication);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL,
+ ActivityThread::currentApplication);
} else {
ImeTracker.forLatency().onHideCancelled(statsToken,
- PHASE_CLIENT_ANIMATION_CANCEL, ActivityThread::currentApplication);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL,
+ ActivityThread::currentApplication);
}
ImeTracker.forLogging().onCancelled(statsToken,
ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION);
@@ -1602,12 +1612,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
if (invokeCallback) {
ImeTracker.forLogging().onCancelled(control.getStatsToken(),
- PHASE_CLIENT_ANIMATION_CANCEL);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL);
control.cancel();
} else {
// Succeeds if invokeCallback is false (i.e. when called from notifyFinished).
ImeTracker.forLogging().onProgress(control.getStatsToken(),
- PHASE_CLIENT_ANIMATION_CANCEL);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL);
}
if (DEBUG) {
Log.d(TAG, TextUtils.formatSimple(
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index bffaeea6a731..ebdddd537ae3 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -29,6 +29,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.SparseArray;
@@ -92,6 +93,7 @@ public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner
}
@Override
+ @Nullable
public ImeTracker.Token getStatsToken() {
// Return null as resizing the IME view is not explicitly tracked.
return null;
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 0ce61bb17774..fdb2a6ee1791 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -314,7 +314,7 @@ public class InsetsSourceConsumer {
* @param fromController {@code true} if request is coming from controller.
* (e.g. in IME case, controller is
* {@link android.inputmethodservice.InputMethodService}).
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*
* @implNote The {@code statsToken} is ignored here, and only handled in
* {@link ImeInsetsSourceConsumer} for IME animations only.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 68e8c726a209..67a397828fa0 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1733,6 +1733,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
boolean keepHistory);
@CriticalNative
+ private static native long nativeSplit(long destNativePtr, long sourceNativePtr, int idBits);
+ @CriticalNative
private static native int nativeGetId(long nativePtr);
@CriticalNative
private static native int nativeGetDeviceId(long nativePtr);
@@ -3767,86 +3769,23 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * Splits a motion event such that it includes only a subset of pointer ids.
+ * Splits a motion event such that it includes only a subset of pointer IDs.
+ * @param idBits the bitset indicating all of the pointer IDs from this motion event that should
+ * be in the new split event. idBits must be a non-empty subset of the pointer IDs
+ * contained in this event.
* @hide
*/
+ // TODO(b/327503168): Pass downTime as a parameter to split.
@UnsupportedAppUsage
+ @NonNull
public final MotionEvent split(int idBits) {
- MotionEvent ev = obtain();
- synchronized (gSharedTempLock) {
- final int oldPointerCount = nativeGetPointerCount(mNativePtr);
- ensureSharedTempPointerCapacity(oldPointerCount);
- final PointerProperties[] pp = gSharedTempPointerProperties;
- final PointerCoords[] pc = gSharedTempPointerCoords;
- final int[] map = gSharedTempPointerIndexMap;
-
- final int oldAction = nativeGetAction(mNativePtr);
- final int oldActionMasked = oldAction & ACTION_MASK;
- final int oldActionPointerIndex = (oldAction & ACTION_POINTER_INDEX_MASK)
- >> ACTION_POINTER_INDEX_SHIFT;
- int newActionPointerIndex = -1;
- int newPointerCount = 0;
- for (int i = 0; i < oldPointerCount; i++) {
- nativeGetPointerProperties(mNativePtr, i, pp[newPointerCount]);
- final int idBit = 1 << pp[newPointerCount].id;
- if ((idBit & idBits) != 0) {
- if (i == oldActionPointerIndex) {
- newActionPointerIndex = newPointerCount;
- }
- map[newPointerCount] = i;
- newPointerCount += 1;
- }
- }
-
- if (newPointerCount == 0) {
- throw new IllegalArgumentException("idBits did not match any ids in the event");
- }
-
- final int newAction;
- if (oldActionMasked == ACTION_POINTER_DOWN || oldActionMasked == ACTION_POINTER_UP) {
- if (newActionPointerIndex < 0) {
- // An unrelated pointer changed.
- newAction = ACTION_MOVE;
- } else if (newPointerCount == 1) {
- // The first/last pointer went down/up.
- newAction = oldActionMasked == ACTION_POINTER_DOWN
- ? ACTION_DOWN
- : (getFlags() & FLAG_CANCELED) == 0 ? ACTION_UP : ACTION_CANCEL;
- } else {
- // A secondary pointer went down/up.
- newAction = oldActionMasked
- | (newActionPointerIndex << ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- // Simple up/down/cancel/move or other motion action.
- newAction = oldAction;
- }
-
- final int historySize = nativeGetHistorySize(mNativePtr);
- for (int h = 0; h <= historySize; h++) {
- final int historyPos = h == historySize ? HISTORY_CURRENT : h;
-
- for (int i = 0; i < newPointerCount; i++) {
- nativeGetPointerCoords(mNativePtr, map[i], historyPos, pc[i]);
- }
-
- final long eventTimeNanos = nativeGetEventTimeNanos(mNativePtr, historyPos);
- if (h == 0) {
- ev.initialize(nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr),
- nativeGetDisplayId(mNativePtr),
- newAction, nativeGetFlags(mNativePtr),
- nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
- nativeGetButtonState(mNativePtr), nativeGetClassification(mNativePtr),
- nativeGetXOffset(mNativePtr), nativeGetYOffset(mNativePtr),
- nativeGetXPrecision(mNativePtr), nativeGetYPrecision(mNativePtr),
- nativeGetDownTimeNanos(mNativePtr), eventTimeNanos,
- newPointerCount, pp, pc);
- } else {
- nativeAddBatch(ev.mNativePtr, eventTimeNanos, pc, 0);
- }
- }
- return ev;
+ if (idBits == 0) {
+ throw new IllegalArgumentException(
+ "idBits must contain at least one pointer from this motion event");
}
+ MotionEvent event = obtain();
+ event.mNativePtr = nativeSplit(event.mNativePtr, this.mNativePtr, idBits);
+ return event;
}
/**
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index f5f4fd9f7042..9099f9855eab 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -428,13 +428,11 @@ public final class PointerIcon implements Parcelable {
private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
VectorDrawable vectorDrawable) {
- Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
- vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- // BitmapDrawables and Bitmap have a default density of DisplayMetrics.DENSITY_DEVICE,
- // (which is deprecated in favor of DENSITY_DEVICE_STABLE/resources.densityDpi). In
- // rare cases when device density differs from the resource density, the bitmap will
- // scale as the BitmapDrawable is created. Avoid by explicitly setting density here.
- bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
+ // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized
+ // with the correct density.
+ Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(),
+ vectorDrawable.getIntrinsicWidth(),
+ vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
diff --git a/core/java/android/view/ScrollFeedbackProvider.java b/core/java/android/view/ScrollFeedbackProvider.java
index 8a44d4fa5934..798203fadb4d 100644
--- a/core/java/android/view/ScrollFeedbackProvider.java
+++ b/core/java/android/view/ScrollFeedbackProvider.java
@@ -21,8 +21,11 @@ import android.annotation.NonNull;
import android.view.flags.Flags;
/**
- * Interface to represent an entity giving consistent feedback for different events surrounding view
- * scroll.
+ * Provides feedback to the user for scroll events on a {@link View}. The type of feedback provided
+ * to the user may depend on the {@link InputDevice} that generated the scroll events.
+ *
+ * <p>An example of the type of feedback that this interface may provide is haptic feedback (that
+ * is, tactile feedback that provide the user physical feedback for their scroll).
*
* <p>The interface provides methods for the client to report different scroll events. The client
* should report all scroll events that they want to be considered for scroll feedback using the
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 042af1f0fb15..828004b6b235 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25,6 +25,7 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -10404,7 +10405,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Sets content sensitivity mode to determine whether this view displays sensitive content.
+ * Sets content sensitivity mode to determine whether this view displays sensitive content
+ * (e.g. username, password etc.). The system may improve user privacy i.e. hide content
+ * drawn by a sensitive view from screen sharing and recording.
*
* @param mode {@link #CONTENT_SENSITIVITY_AUTO}, {@link #CONTENT_SENSITIVITY_NOT_SENSITIVE}
* or {@link #CONTENT_SENSITIVITY_SENSITIVE}
@@ -16815,10 +16818,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo.mViewRootImpl.getWindowVisibleDisplayFrame(outRect);
return;
}
- // The view is not attached to a display so we don't have a context.
- // Make a best guess about the display size.
- Display d = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
- d.getRectSize(outRect);
+ // TODO (b/327559224): Refine the behavior to better reflect the window environment with API
+ // doc updates.
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ final WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
+ final Insets insets = metrics.getWindowInsets().getInsets(
+ WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
+ outRect.set(metrics.getBounds());
+ outRect.inset(insets);
+ outRect.offsetTo(0, 0);
}
/**
@@ -28371,15 +28379,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
final boolean always = (flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0;
+ boolean fromIme = false;
+ if (mAttachInfo.mViewRootImpl != null) {
+ fromIme = mAttachInfo.mViewRootImpl.mWindowAttributes.type == TYPE_INPUT_METHOD;
+ }
if (Flags.useVibratorHapticFeedback()) {
if (!mAttachInfo.canPerformHapticFeedback()) {
return false;
}
getSystemVibrator().performHapticFeedback(
- feedbackConstant, always, "View#performHapticFeedback");
+ feedbackConstant, always, "View#performHapticFeedback", fromIme);
return true;
}
- return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant, always);
+ return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant, always, fromIme);
}
private Vibrator getSystemVibrator() {
@@ -31422,7 +31434,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
interface Callbacks {
void playSoundEffect(int effectId);
- boolean performHapticFeedback(int effectId, boolean always);
+ boolean performHapticFeedback(int effectId, boolean always, boolean fromIme);
}
/**
@@ -33770,8 +33782,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis;
mMinusOneFrameIntervalMillis = timeIntervalMillis;
- if (mMinusOneFrameIntervalMillis - mMinusTwoFrameIntervalMillis >= 30
- && timeIntervalMillis < 2) {
+ mLastUpdateTimeMillis = currentTimeMillis;
+ if (mMinusTwoFrameIntervalMillis >= 30 && timeIntervalMillis < 2) {
return;
}
if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 333cbb39d9c7..02f8e6e9b810 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1210,6 +1210,9 @@ public final class ViewRootImpl implements ViewParent,
mSensitiveContentProtectionService =
ISensitiveContentProtectionManager.Stub.asInterface(
ServiceManager.getService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE));
+ if (mSensitiveContentProtectionService == null) {
+ Log.e(TAG, "SensitiveContentProtectionService shouldn't be null");
+ }
} else {
mSensitiveContentProtectionService = null;
}
@@ -3444,7 +3447,10 @@ public final class ViewRootImpl implements ViewParent,
// other windows to resize/move based on the raw frame of the window, waiting until we
// can finish laying out this window and get back to the window manager with the
// ultimately computed insets.
- insetsPending = computesInternalInsets;
+ insetsPending = computesInternalInsets
+ // If this window provides insets via params, its insets source frame can be
+ // updated directly without waiting for WindowSession#setInsets.
+ && mWindowAttributes.providedInsets == null;
if (mSurfaceHolder != null) {
mSurfaceHolder.mSurfaceLock.lock();
@@ -4179,6 +4185,9 @@ public final class ViewRootImpl implements ViewParent,
*/
void notifySensitiveContentAppProtection(boolean showSensitiveContent) {
try {
+ if (mSensitiveContentProtectionService == null) {
+ return;
+ }
// The window would be blocked during screen share if it shows sensitive content.
mSensitiveContentProtectionService.setSensitiveContentProtection(
getWindowToken(), mContext.getPackageName(), showSensitiveContent);
@@ -9201,18 +9210,18 @@ public final class ViewRootImpl implements ViewParent,
* {@inheritDoc}
*/
@Override
- public boolean performHapticFeedback(int effectId, boolean always) {
+ public boolean performHapticFeedback(int effectId, boolean always, boolean fromIme) {
if ((mDisplay.getFlags() & Display.FLAG_TOUCH_FEEDBACK_DISABLED) != 0) {
return false;
}
try {
if (USE_ASYNC_PERFORM_HAPTIC_FEEDBACK) {
- mWindowSession.performHapticFeedbackAsync(effectId, always);
+ mWindowSession.performHapticFeedbackAsync(effectId, always, fromIme);
return true;
} else {
// Original blocking binder call path.
- return mWindowSession.performHapticFeedback(effectId, always);
+ return mWindowSession.performHapticFeedback(effectId, always, fromIme);
}
} catch (RemoteException e) {
return false;
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index fbebe1e726b7..561d979f2f9d 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -528,28 +528,7 @@ public final class WindowInsets {
@FlaggedApi(Flags.FLAG_CUSTOMIZABLE_WINDOW_HEADERS)
@NonNull
public List<Rect> getBoundingRects(@InsetsType int typeMask) {
- Rect[] allRects = null;
- for (int i = FIRST; i <= LAST; i = i << 1) {
- if ((typeMask & i) == 0) {
- continue;
- }
- final Rect[] rects = mTypeBoundingRectsMap[indexOf(i)];
- if (rects == null) {
- continue;
- }
- if (allRects == null) {
- allRects = rects;
- } else {
- final Rect[] concat = new Rect[allRects.length + rects.length];
- System.arraycopy(allRects, 0, concat, 0, allRects.length);
- System.arraycopy(rects, 0, concat, allRects.length, rects.length);
- allRects = concat;
- }
- }
- if (allRects == null) {
- return Collections.emptyList();
- }
- return Arrays.asList(allRects);
+ return getBoundingRects(mTypeBoundingRectsMap, typeMask);
}
/**
@@ -577,16 +556,28 @@ public final class WindowInsets {
*
* @param typeMask the insets type for which to obtain the bounding rectangles
* @return the bounding rectangles
+ * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Bounding
+ * rects are not available if the IME isn't visible as the
+ * height of the IME is dynamic depending on the
+ * {@link EditorInfo} of the currently focused view, as well
+ * as the UI state of the IME.
*/
@FlaggedApi(Flags.FLAG_CUSTOMIZABLE_WINDOW_HEADERS)
@NonNull
public List<Rect> getBoundingRectsIgnoringVisibility(@InsetsType int typeMask) {
+ if ((typeMask & IME) != 0) {
+ throw new IllegalArgumentException("Unable to query the bounding rects for IME");
+ }
+ return getBoundingRects(mTypeMaxBoundingRectsMap, typeMask);
+ }
+
+ private List<Rect> getBoundingRects(Rect[][] typeBoundingRectsMap, @InsetsType int typeMask) {
Rect[] allRects = null;
for (int i = FIRST; i <= LAST; i = i << 1) {
if ((typeMask & i) == 0) {
continue;
}
- final Rect[] rects = mTypeMaxBoundingRectsMap[indexOf(i)];
+ final Rect[] rects = typeBoundingRectsMap[indexOf(i)];
if (rects == null) {
continue;
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 2b2c50725749..22d8ed91d455 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -479,13 +479,13 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public boolean performHapticFeedback(int effectId, boolean always) {
+ public boolean performHapticFeedback(int effectId, boolean always, boolean fromIme) {
return false;
}
@Override
- public void performHapticFeedbackAsync(int effectId, boolean always) {
- performHapticFeedback(effectId, always);
+ public void performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme) {
+ performHapticFeedback(effectId, always, fromIme);
}
@Override
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 64e5a5bb87a2..cf6b9e5b3bae 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1160,12 +1160,10 @@ public final class AutofillManager {
// denylist only applies to not important views
if (!view.isImportantForAutofill() && isActivityDeniedForAutofill()) {
- Log.d(TAG, "view is not autofillable - activity denied for autofill");
return false;
}
if (isActivityAllowedForAutofill()) {
- Log.d(TAG, "view is autofillable - activity allowed for autofill");
return true;
}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 491b0e349cde..cedf8d04ed99 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -25,7 +25,6 @@ import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -298,7 +297,7 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
@@ -315,7 +314,7 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
@Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {
@@ -331,6 +330,20 @@ final class IInputMethodManagerGlobalInvoker {
// TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
@AnyThread
+ @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
+ static void hideSoftInputFromServerForTest() {
+ final IInputMethodManager service = getService();
+ if (service == null) {
+ return;
+ }
+ try {
+ service.hideSoftInputFromServerForTest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @AnyThread
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
static InputBindResult startInputOrWindowGainedFocus(@StartInputReason int startInputReason,
@@ -654,35 +667,18 @@ final class IInputMethodManagerGlobalInvoker {
}
}
- /** @see com.android.server.inputmethod.ImeTrackerService#onRequestShow */
- @AnyThread
- @NonNull
- static ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
- final IImeTracker service = getImeTrackerService();
- if (service == null) {
- // Create token with "fake" binder if the service was not found.
- return new ImeTracker.Token(new Binder(), tag);
- }
- try {
- return service.onRequestShow(tag, uid, origin, reason, fromUser);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** @see com.android.server.inputmethod.ImeTrackerService#onRequestHide */
+ /** @see com.android.server.inputmethod.ImeTrackerService#onStart */
@AnyThread
@NonNull
- static ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
+ static ImeTracker.Token onStart(@NonNull String tag, int uid, @ImeTracker.Type int type,
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
- final IImeTracker service = getImeTrackerService();
+ final var service = getImeTrackerService();
if (service == null) {
- // Create token with "fake" binder if the service was not found.
- return new ImeTracker.Token(new Binder(), tag);
+ // Create token with "empty" binder if the service was not found.
+ return ImeTracker.Token.empty(tag);
}
try {
- return service.onRequestHide(tag, uid, origin, reason, fromUser);
+ return service.onStart(tag, uid, type, origin, reason, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index b1fdaa97ffe0..d992febc375e 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -28,17 +28,20 @@ import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_SHOWN;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityThread;
import android.content.Context;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.os.SystemProperties;
import android.util.Log;
import android.view.InsetsController.AnimationType;
import android.view.SurfaceControl;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.jank.InteractionJankMonitor;
@@ -108,34 +111,32 @@ public interface ImeTracker {
/**
* The origin of the IME request
*
- * The name follows the format {@code PHASE_x_...} where {@code x} denotes
- * where the origin is (i.e. {@code PHASE_SERVER_...} occurs in the server).
+ * <p> The name follows the format {@code ORIGIN_x_...} where {@code x} denotes
+ * where the origin is (i.e. {@code ORIGIN_SERVER} occurs in the server).
*/
@IntDef(prefix = { "ORIGIN_" }, value = {
- ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- ORIGIN_SERVER_START_INPUT,
- ORIGIN_SERVER_HIDE_INPUT
+ ORIGIN_CLIENT,
+ ORIGIN_SERVER,
+ ORIGIN_IME
})
@Retention(RetentionPolicy.SOURCE)
@interface Origin {}
- /** The IME show request originated in the client. */
- int ORIGIN_CLIENT_SHOW_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_SHOW_SOFT_INPUT;
+ /** The IME request originated in the client. */
+ int ORIGIN_CLIENT = ImeProtoEnums.ORIGIN_CLIENT;
- /** The IME hide request originated in the client. */
- int ORIGIN_CLIENT_HIDE_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_HIDE_SOFT_INPUT;
+ /** The IME request originated in the server. */
+ int ORIGIN_SERVER = ImeProtoEnums.ORIGIN_SERVER;
- /** The IME show request originated in the server. */
- int ORIGIN_SERVER_START_INPUT = ImeProtoEnums.ORIGIN_SERVER_START_INPUT;
-
- /** The IME hide request originated in the server. */
- int ORIGIN_SERVER_HIDE_INPUT = ImeProtoEnums.ORIGIN_SERVER_HIDE_INPUT;
+ /** The IME request originated in the IME. */
+ int ORIGIN_IME = ImeProtoEnums.ORIGIN_IME;
+ /** The IME request originated in the WindowManager Shell. */
+ int ORIGIN_WM_SHELL = ImeProtoEnums.ORIGIN_WM_SHELL;
/**
* The current phase of the IME request.
*
- * The name follows the format {@code PHASE_x_...} where {@code x} denotes
+ * <p> The name follows the format {@code PHASE_x_...} where {@code x} denotes
* where the phase is (i.e. {@code PHASE_SERVER_...} occurs in the server).
*/
@IntDef(prefix = { "PHASE_" }, value = {
@@ -155,7 +156,6 @@ public interface ImeTracker {
PHASE_IME_SHOW_SOFT_INPUT,
PHASE_IME_HIDE_SOFT_INPUT,
PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE,
- PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER,
PHASE_SERVER_APPLY_IME_VISIBILITY,
PHASE_WM_SHOW_IME_RUNNER,
PHASE_WM_SHOW_IME_READY,
@@ -182,6 +182,10 @@ public interface ImeTracker {
PHASE_CLIENT_ANIMATION_FINISHED_SHOW,
PHASE_CLIENT_ANIMATION_FINISHED_HIDE,
PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT,
+ PHASE_IME_SHOW_WINDOW,
+ PHASE_IME_HIDE_WINDOW,
+ PHASE_IME_PRIVILEGED_OPERATIONS,
+ PHASE_SERVER_CURRENT_ACTIVE_IME,
})
@Retention(RetentionPolicy.SOURCE)
@interface Phase {}
@@ -224,19 +228,15 @@ public interface ImeTracker {
/** Dispatched from the IME wrapper to the IME. */
int PHASE_IME_WRAPPER_DISPATCH = ImeProtoEnums.PHASE_IME_WRAPPER_DISPATCH;
- /** Reached the IME' showSoftInput method. */
+ /** Reached the IME's showSoftInput method. */
int PHASE_IME_SHOW_SOFT_INPUT = ImeProtoEnums.PHASE_IME_SHOW_SOFT_INPUT;
- /** Reached the IME' hideSoftInput method. */
+ /** Reached the IME's hideSoftInput method. */
int PHASE_IME_HIDE_SOFT_INPUT = ImeProtoEnums.PHASE_IME_HIDE_SOFT_INPUT;
/** The server decided the IME should be shown. */
int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = ImeProtoEnums.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE;
- /** Requested applying the IME visibility in the insets source consumer. */
- int PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER =
- ImeProtoEnums.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER;
-
/** Applied the IME visibility. */
int PHASE_SERVER_APPLY_IME_VISIBILITY = ImeProtoEnums.PHASE_SERVER_APPLY_IME_VISIBILITY;
@@ -323,37 +323,49 @@ public interface ImeTracker {
int PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT =
ImeProtoEnums.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT;
+ /** Reached the IME's showWindow method. */
+ int PHASE_IME_SHOW_WINDOW = ImeProtoEnums.PHASE_IME_SHOW_WINDOW;
+
+ /** Reached the IME's hideWindow method. */
+ int PHASE_IME_HIDE_WINDOW = ImeProtoEnums.PHASE_IME_HIDE_WINDOW;
+
+ /** Reached the InputMethodPrivilegedOperations handler. */
+ int PHASE_IME_PRIVILEGED_OPERATIONS = ImeProtoEnums.PHASE_IME_PRIVILEGED_OPERATIONS;
+
+ /** Checked that the calling IME is the currently active IME. */
+ int PHASE_SERVER_CURRENT_ACTIVE_IME = ImeProtoEnums.PHASE_SERVER_CURRENT_ACTIVE_IME;
+
/**
- * Creates an IME show request tracking token.
+ * Called when an IME request is started.
*
- * @param component the name of the component that created the IME request, or {@code null}
- * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME show request.
- * @param reason the reason why the IME show request was created.
+ * @param component the name of the component that started the request.
+ * @param uid the uid of the client that started the request.
+ * @param type the type of the request.
+ * @param origin the origin of the request.
+ * @param reason the reason for starting the request.
* @param fromUser whether this request was created directly from user interaction.
*
- * @return An IME tracking token.
+ * @return An IME request tracking token.
*/
@NonNull
- Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
+ Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
@SoftInputShowHideReason int reason, boolean fromUser);
/**
- * Creates an IME hide request tracking token.
+ * Called when an IME request is started for the current process.
*
- * @param component the name of the component that created the IME request, or {@code null}
- * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME hide request.
- * @param reason the reason why the IME hide request was created.
+ * @param type the type of the request.
+ * @param origin the origin of the request.
+ * @param reason the reason for starting the request.
* @param fromUser whether this request was created directly from user interaction.
*
- * @return An IME tracking token.
+ * @return An IME request tracking token.
*/
@NonNull
- Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason, boolean fromUser);
+ default Token onStart(@Type int type, @Origin int origin, @SoftInputShowHideReason int reason,
+ boolean fromUser) {
+ return onStart(Process.myProcessName(), Process.myUid(), type, origin, reason, fromUser);
+ }
/**
* Called when an IME request progresses to a further phase.
@@ -390,14 +402,14 @@ public interface ImeTracker {
/**
* Called when the IME show request is successful.
*
- * @param token the token tracking the current IME show request or {@code null} otherwise.
+ * @param token the token tracking the current IME request or {@code null} otherwise.
*/
void onShown(@Nullable Token token);
/**
* Called when the IME hide request is successful.
*
- * @param token the token tracking the current IME hide request or {@code null} otherwise.
+ * @param token the token tracking the current IME request or {@code null} otherwise.
*/
void onHidden(@Nullable Token token);
@@ -479,33 +491,17 @@ public interface ImeTracker {
@NonNull
@Override
- public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason, boolean fromUser) {
- final var tag = getTag(component);
- final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin,
- reason, fromUser);
-
- Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
- + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
- + " fromUser " + fromUser,
- mLogStackTrace ? new Throwable() : null);
-
- return token;
- }
-
- @NonNull
- @Override
- public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
+ public Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
@SoftInputShowHideReason int reason, boolean fromUser) {
- final var tag = getTag(component);
- final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin,
- reason, fromUser);
+ final var tag = Token.createTag(component);
+ final var token = IInputMethodManagerGlobalInvoker.onStart(tag, uid, type,
+ origin, reason, fromUser);
- Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
+ Log.i(TAG, token.mTag + ": onRequest" + (type == TYPE_SHOW ? "Show" : "Hide")
+ + " at " + Debug.originToString(origin)
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
+ " fromUser " + fromUser,
mLogStackTrace ? new Throwable() : null);
-
return token;
}
@@ -556,20 +552,6 @@ public interface ImeTracker {
Log.i(TAG, token.mTag + ": onHidden");
}
-
- /**
- * Returns a logging tag using the given component name.
- *
- * @param component the name of the component that created the IME request, or {@code null}
- * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
- */
- @NonNull
- private String getTag(@Nullable String component) {
- if (component == null) {
- component = ActivityThread.currentProcessName();
- }
- return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
- }
};
/** The singleton IME tracker instance for instrumenting jank metrics. */
@@ -581,6 +563,10 @@ public interface ImeTracker {
/** A token that tracks the progress of an IME request. */
final class Token implements Parcelable {
+ /** Empty binder, lazily initialized, used for empty token instantiation. */
+ @Nullable
+ private static IBinder sEmptyBinder;
+
/** The binder used to identify this token. */
@NonNull
private final IBinder mBinder;
@@ -599,16 +585,56 @@ public interface ImeTracker {
mTag = in.readString8();
}
+ /** Returns the binder used to identify this token. */
@NonNull
public IBinder getBinder() {
return mBinder;
}
+ /** Returns the logging tag of this token. */
@NonNull
public String getTag() {
return mTag;
}
+ /**
+ * Creates a logging tag.
+ *
+ * @param component the name of the component that created the IME request.
+ */
+ @NonNull
+ private static String createTag(@NonNull String component) {
+ return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
+ }
+
+ /** Returns a new token with an empty binder. */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public static Token empty() {
+ final var tag = createTag(Process.myProcessName());
+ return empty(tag);
+ }
+
+ /** Returns a new token with an empty binder and the given logging tag. */
+ @NonNull
+ static Token empty(@NonNull String tag) {
+ return new Token(getEmptyBinder(), tag);
+ }
+
+ /** Returns the empty binder instance for empty token creation, lazily initializing it. */
+ @NonNull
+ private static IBinder getEmptyBinder() {
+ if (sEmptyBinder == null) {
+ sEmptyBinder = new Binder();
+ }
+ return sEmptyBinder;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "(tag: " + mTag + ")";
+ }
+
/** For Parcelable, no special marshalled objects. */
@Override
public int describeContents() {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 33f34c5697c4..88607fc80f69 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -281,7 +281,7 @@ public interface InputMethod {
})
@Retention(RetentionPolicy.SOURCE)
@interface ShowFlags {}
-
+
/**
* Flag for {@link #showSoftInput}: this show has been explicitly
* requested by the user. If not set, the system has decided it may be
@@ -314,18 +314,18 @@ public interface InputMethod {
* @param showInputToken an opaque {@link android.os.Binder} token to identify which API call
* of {@link InputMethodManager#showSoftInput(View, int)} is associated with
* this callback.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
* @hide
*/
@MainThread
public default void showSoftInputWithToken(@ShowFlags int flags, ResultReceiver resultReceiver,
- IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
+ IBinder showInputToken, @NonNull ImeTracker.Token statsToken) {
showSoftInput(flags, resultReceiver);
}
/**
* Request that any soft input part of the input method be shown to the user.
- *
+ *
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
* The result code should be
@@ -352,12 +352,12 @@ public interface InputMethod {
* @param hideInputToken an opaque {@link android.os.Binder} token to identify which API call
* of {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}} is associated
* with this callback.
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
* @hide
*/
@MainThread
public default void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) {
+ IBinder hideInputToken, @NonNull ImeTracker.Token statsToken) {
hideSoftInput(flags, resultReceiver);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fcc8344cbcd9..985f542c9982 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2262,21 +2262,22 @@ public final class InputMethodManager {
* {@link #RESULT_HIDDEN}.
*/
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
- return showSoftInput(view, null /* statsToken */, flags, resultReceiver,
- SoftInputShowHideReason.SHOW_SOFT_INPUT);
+ return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
- private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken,
- @ShowFlags int flags, ResultReceiver resultReceiver,
+ private boolean showSoftInput(View view, @ShowFlags int flags,
+ @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ // TODO(b/303041796): handle tracking physical keyboard and DPAD as user interactions
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(view));
+ return showSoftInput(view, statsToken, flags, resultReceiver, reason);
+ }
+
+ private boolean showSoftInput(View view, @NonNull ImeTracker.Token statsToken,
+ @ShowFlags int flags, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
- if (statsToken == null) {
- // TODO(b/303041796): handle tracking physical keyboard and DPAD as user interactions
- statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason,
- ImeTracker.isFromUser(view));
- }
- ImeTracker.forLatency().onRequestShow(statsToken, ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- reason, ActivityThread::currentApplication);
+ ImeTracker.forLatency().onRequestShow(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this,
null /* icProto */);
// Re-dispatch if there is a context mismatch.
@@ -2289,9 +2290,8 @@ public final class InputMethodManager {
synchronized (mH) {
if (!hasServedByInputMethodLocked(view)) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
- ImeTracker.forLatency().onShowFailed(
- statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED,
- ActivityThread::currentApplication);
+ ImeTracker.forLatency().onShowFailed(statsToken,
+ ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication);
Log.w(TAG, "Ignoring showSoftInput() as view=" + view + " is not served.");
return false;
}
@@ -2326,9 +2326,9 @@ public final class InputMethodManager {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499)
public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) {
synchronized (mH) {
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- SoftInputShowHideReason.SHOW_SOFT_INPUT, false /* fromUser */);
+ final int reason = SoftInputShowHideReason.SHOW_SOFT_INPUT;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be"
+ " removed soon. If you are using androidx.appcompat.widget.SearchView,"
@@ -2352,7 +2352,7 @@ public final class InputMethodManager {
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- SoftInputShowHideReason.SHOW_SOFT_INPUT);
+ reason);
}
}
@@ -2428,11 +2428,10 @@ public final class InputMethodManager {
initialServedView = getServedViewLocked();
}
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ImeTracker.isFromUser(initialServedView));
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ActivityThread::currentApplication);
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(initialServedView));
+ ImeTracker.forLatency().onRequestHide(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow",
this, null /* icProto */);
checkFocus();
@@ -2471,20 +2470,18 @@ public final class InputMethodManager {
}
}
- final var reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW;
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ImeTracker.isFromUser(view));
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ActivityThread::currentApplication);
+ final int reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(view));
+ ImeTracker.forLatency().onRequestHide(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromView",
this, null /* icProto */);
synchronized (mH) {
if (!hasServedByInputMethodLocked(view)) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
- ImeTracker.forLatency().onShowFailed(
- statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED,
- ActivityThread::currentApplication);
+ ImeTracker.forLatency().onShowFailed(statsToken,
+ ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication);
Log.w(TAG, "Ignoring hideSoftInputFromView() as view=" + view + " is not served.");
return false;
}
@@ -2497,6 +2494,19 @@ public final class InputMethodManager {
}
/**
+ * A test API for CTS to request hiding the current soft input window, with the request origin
+ * on the server side.
+ *
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @TestApi
+ @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() {
+ IInputMethodManagerGlobalInvoker.hideSoftInputFromServerForTest();
+ }
+
+ /**
* Start stylus handwriting session.
*
* If supported by the current input method, a stylus handwriting session is started on the
@@ -2898,8 +2908,6 @@ public final class InputMethodManager {
* @param flags {@link #HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED} or {@code 0}
* @param executor The executor to run the callback on.
* @param callback {@code true>} would be received if delegation was accepted.
- * @return {@code true} if view belongs to allowed delegate package declared in {@link
- * #prepareStylusHandwritingDelegation(View, String)} and delegation is accepted
* @see #prepareStylusHandwritingDelegation(View, String)
* @see #acceptStylusHandwritingDelegation(View)
*/
@@ -2972,10 +2980,11 @@ public final class InputMethodManager {
if (view != null) {
final WindowInsets rootInsets = view.getRootWindowInsets();
if (rootInsets != null && rootInsets.isVisible(WindowInsets.Type.ime())) {
- hideSoftInputFromWindow(view.getWindowToken(), hideFlags, null,
+ hideSoftInputFromWindow(view.getWindowToken(), hideFlags,
+ null /* resultReceiver */,
SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT);
} else {
- showSoftInput(view, null /* statsToken */, showFlags, null /* resultReceiver */,
+ showSoftInput(view, showFlags, null /* resultReceiver */,
SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT);
}
}
@@ -3536,11 +3545,11 @@ public final class InputMethodManager {
@UnsupportedAppUsage
void closeCurrentInput() {
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION, false /* fromUser */);
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION,
+ final int reason = SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
+ ImeTracker.forLatency().onRequestHide(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason,
ActivityThread::currentApplication);
synchronized (mH) {
@@ -3561,7 +3570,7 @@ public final class InputMethodManager {
statsToken,
HIDE_NOT_ALWAYS,
null,
- SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION);
+ reason);
}
}
@@ -3602,12 +3611,12 @@ public final class InputMethodManager {
*
* @param windowToken the window from which this request originates. If this doesn't match the
* currently served view, the request is ignored and returns {@code false}.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*
* @return {@code true} if IME can (eventually) be shown, {@code false} otherwise.
* @hide
*/
- public boolean requestImeShow(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
+ public boolean requestImeShow(IBinder windowToken, @NonNull ImeTracker.Token statsToken) {
checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
@@ -3631,16 +3640,11 @@ public final class InputMethodManager {
*
* @param windowToken the window from which this request originates. If this doesn't match the
* currently served view, the request is ignored.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
* @hide
*/
- public void notifyImeHidden(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
- if (statsToken == null) {
- statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, false /* fromUser */);
- }
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ public void notifyImeHidden(IBinder windowToken, @NonNull ImeTracker.Token statsToken) {
+ ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT,
SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this,
@@ -4024,8 +4028,11 @@ public final class InputMethodManager {
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) {
- InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(
- flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION);
+ final int reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
+ InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(statsToken, flags,
+ reason);
}
/**
@@ -4043,7 +4050,11 @@ public final class InputMethodManager {
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) {
- InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
+ final int reason = SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
+ InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(statsToken, flags,
+ reason);
}
/**
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 3fc0a305c8e6..8501474b70a6 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -175,8 +175,16 @@ public final class WebViewDelegate {
/**
* Adds the WebView asset path to {@link android.content.res.AssetManager}.
+ * If {@link android.content.res.Flags#FLAG_REGISTER_RESOURCE_PATHS} is enabled, this function
+ * will be a no-op because the asset paths appending work will only be handled by
+ * {@link android.content.res.Resources#registerResourcePaths(String, ApplicationInfo)},
+ * otherwise it behaves the old way.
*/
public void addWebViewAssetPath(Context context) {
+ if (android.content.res.Flags.registerResourcePaths()) {
+ return;
+ }
+
final String[] newAssetPaths =
WebViewFactory.getLoadedPackageInfo().applicationInfo.getAllApkPaths();
final ApplicationInfo appInfo = context.getApplicationInfo();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index c748a57dce74..8f1b72e90da1 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
+import android.content.res.Resources;
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -93,6 +94,9 @@ public final class WebViewFactory {
// error for namespace lookup
public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10;
+ // generic error for future use
+ static final int LIBLOAD_FAILED_OTHER = 11;
+
/**
* Stores the timestamps at which various WebView startup events occurred in this process.
*/
@@ -544,8 +548,14 @@ public final class WebViewFactory {
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
sTimestamps.mAddAssetsStart = SystemClock.uptimeMillis();
- for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
- initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
+ if (android.content.res.Flags.registerResourcePaths()) {
+ Resources.registerResourcePaths(webViewContext.getPackageName(),
+ webViewContext.getApplicationInfo());
+ } else {
+ for (String newAssetPath : webViewContext.getApplicationInfo()
+ .getAllApkPaths()) {
+ initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
+ }
}
sTimestamps.mAddAssetsEnd = sTimestamps.mGetClassLoaderStart =
SystemClock.uptimeMillis();
diff --git a/core/java/android/webkit/WebViewProviderResponse.java b/core/java/android/webkit/WebViewProviderResponse.java
index 84e34a34f7f7..926aaff782f3 100644
--- a/core/java/android/webkit/WebViewProviderResponse.java
+++ b/core/java/android/webkit/WebViewProviderResponse.java
@@ -40,6 +40,7 @@ public final class WebViewProviderResponse implements Parcelable {
STATUS_SUCCESS,
STATUS_FAILED_WAITING_FOR_RELRO,
STATUS_FAILED_LISTING_WEBVIEW_PACKAGES,
+ STATUS_FAILED_OTHER,
})
@Retention(RetentionPolicy.SOURCE)
private @interface WebViewProviderStatus {}
@@ -49,6 +50,7 @@ public final class WebViewProviderResponse implements Parcelable {
WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
public static final int STATUS_FAILED_LISTING_WEBVIEW_PACKAGES =
WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+ public static final int STATUS_FAILED_OTHER = WebViewFactory.LIBLOAD_FAILED_OTHER;
public WebViewProviderResponse(
@Nullable PackageInfo packageInfo, @WebViewProviderStatus int status) {
diff --git a/core/java/android/webkit/WebViewUpdateManager.java b/core/java/android/webkit/WebViewUpdateManager.java
index 8ada598d8a76..07576a2e89e4 100644
--- a/core/java/android/webkit/WebViewUpdateManager.java
+++ b/core/java/android/webkit/WebViewUpdateManager.java
@@ -127,7 +127,7 @@ public final class WebViewUpdateManager {
*
* This choice will be stored persistently.
*
- * @param newProvider the package name to use, or null to reset to default.
+ * @param newProvider the package name to use.
* @return the package name which is now in use, which may not be the
* requested one if it was not usable.
*/
@@ -155,7 +155,7 @@ public final class WebViewUpdateManager {
/**
* Get the WebView provider which will be used if no explicit choice has been made.
*
- * The default provider is not guaranteed to be currently valid/usable.
+ * The default provider is not guaranteed to be a valid/usable WebView implementation.
*
* @return the default WebView provider.
*/
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 55b2251ac196..0b99df323b09 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.flags.Flags.viewVelocityApi;
+
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1488,6 +1490,11 @@ public class HorizontalScrollView extends FrameLayout {
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
+
+ // For variable refresh rate project to track the current velocity of this View
+ if (viewVelocityApi()) {
+ setFrameContentVelocity(Math.abs(mScroller.getCurrVelocity()));
+ }
}
}
@@ -1810,6 +1817,11 @@ public class HorizontalScrollView extends FrameLayout {
mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0,
maxScroll, 0, 0, width / 2, 0);
+ // For variable refresh rate project to track the current velocity of this View
+ if (viewVelocityApi()) {
+ setFrameContentVelocity(Math.abs(mScroller.getCurrVelocity()));
+ }
+
final boolean movingRight = velocityX > 0;
View currentFocused = findFocus();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 0e5747d0e445..fe4ac4e2f0f1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1105,6 +1105,7 @@ public class RemoteViews implements Parcelable, Filter {
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
mViewId = parcel.readInt();
mIntentId = parcel.readInt();
+ mIsReplacedIntoAction = parcel.readBoolean();
mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
mItems = mServiceIntent != null
? null
@@ -1128,6 +1129,7 @@ public class RemoteViews implements Parcelable, Filter {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mViewId);
dest.writeInt(mIntentId);
+ dest.writeBoolean(mIsReplacedIntoAction);
dest.writeTypedObject(mServiceIntent, flags);
if (mItems != null) {
mItems.writeToParcel(dest, flags, /* attached= */ true);
@@ -1209,6 +1211,19 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * The maximum size for RemoteViews with converted RemoteCollectionItemsAdapter.
+ * When converting RemoteViewsAdapter to RemoteCollectionItemsAdapter, we want to put size
+ * limits on each unique RemoteCollectionItems in order to not exceed the transaction size limit
+ * for each parcel (typically 1 MB). We leave a certain ratio of the maximum size as a buffer
+ * for missing calculations of certain parameters (e.g. writing a RemoteCollectionItems to the
+ * parcel will write its Id array as well, but that is missing when writing itschild RemoteViews
+ * directly to the parcel as we did in RemoteViewsService)
+ *
+ * @hide
+ */
+ private static final int MAX_SINGLE_PARCEL_SIZE = (int) (1_000_000 * 0.8);
+
+ /**
* @hide
*/
public CompletableFuture<Void> collectAllIntents() {
@@ -1260,17 +1275,47 @@ public class RemoteViews implements Parcelable, Filter {
return mUriToCollectionMapping.get(uri);
}
- CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
- CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null);
+ public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
+ @NonNull RemoteViews inViews) {
+ SparseArray<Intent> idToIntentMapping = new SparseArray<>();
+ // Collect the number of uinque Intent (which is equal to the number of new connections
+ // to make) for size allocation and exclude certain collections from being written to
+ // the parcel to better estimate the space left for reallocation.
+ collectAllIntentsInternal(inViews, idToIntentMapping);
+
+ // Calculate the individual size here
+ int numOfIntents = idToIntentMapping.size();
+ if (numOfIntents == 0) {
+ Log.e(LOG_TAG, "Possibly notifying updates for nonexistent view Id");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ Parcel sizeTestParcel = Parcel.obtain();
+ // Write self RemoteViews to the parcel, which includes the actions/bitmaps/collection
+ // cache to see how much space is left for the RemoteCollectionItems that are to be
+ // updated.
+ RemoteViews.this.writeToParcel(sizeTestParcel,
+ /* flags= */ 0,
+ /* intentsToIgnore= */ idToIntentMapping);
+ int remainingSize = MAX_SINGLE_PARCEL_SIZE - sizeTestParcel.dataSize();
+ sizeTestParcel.recycle();
+
+ int individualSize = remainingSize < 0
+ ? 0
+ : remainingSize / numOfIntents;
+
+ return connectAllUniqueIntents(individualSize, idToIntentMapping);
+ }
+
+ private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
+ @NonNull SparseArray<Intent> idToIntentMapping) {
if (inViews.hasSizedRemoteViews()) {
for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- collectAllIntentsNoComplete(remoteViews));
+ collectAllIntentsInternal(remoteViews, idToIntentMapping);
}
} else if (inViews.hasLandscapeAndPortraitLayouts()) {
- collectionFuture = CompletableFuture.allOf(
- collectAllIntentsNoComplete(inViews.mLandscape),
- collectAllIntentsNoComplete(inViews.mPortrait));
+ collectAllIntentsInternal(inViews.mLandscape, idToIntentMapping);
+ collectAllIntentsInternal(inViews.mPortrait, idToIntentMapping);
} else if (inViews.mActions != null) {
for (Action action : inViews.mActions) {
if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
@@ -1280,13 +1325,16 @@ public class RemoteViews implements Parcelable, Filter {
}
if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
- final String uri = mIdToUriMapping.get(rca.mIntentId);
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
- .thenAccept(rc -> {
- rc.setHierarchyRootData(getHierarchyRootData());
- mUriToCollectionMapping.put(uri, rc);
- }));
+ rca.mIsReplacedIntoAction = false;
+
+ // Avoid redundant connections for the same intent. Also making sure
+ // that the number of connections we are making is always equal to the
+ // nmuber of unique intents that are being used for the updates.
+ if (idToIntentMapping.contains(rca.mIntentId)) {
+ continue;
+ }
+
+ idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent);
rca.mItems = null;
continue;
}
@@ -1295,7 +1343,7 @@ public class RemoteViews implements Parcelable, Filter {
// intents.
if (rca.mServiceIntent != null) {
final String uri = rca.mServiceIntent.toUri(0);
- int index = mIdToUriMapping.indexOfValue(uri);
+ int index = mIdToUriMapping.indexOfValueByValue(uri);
if (index == -1) {
int newIntentId = mIdToUriMapping.size();
rca.mIntentId = newIntentId;
@@ -1305,41 +1353,50 @@ public class RemoteViews implements Parcelable, Filter {
rca.mItems = null;
continue;
}
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
- .thenAccept(rc -> {
- rc.setHierarchyRootData(getHierarchyRootData());
- mUriToCollectionMapping.put(uri, rc);
- }));
+
+ idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent);
rca.mItems = null;
} else {
for (RemoteViews views : rca.mItems.mViews) {
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- collectAllIntentsNoComplete(views));
+ collectAllIntentsInternal(views, idToIntentMapping);
}
}
} else if (action instanceof ViewGroupActionAdd vgaa
&& vgaa.mNestedViews != null) {
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- collectAllIntentsNoComplete(vgaa.mNestedViews));
+ collectAllIntentsInternal(vgaa.mNestedViews, idToIntentMapping);
}
}
}
+ }
- return collectionFuture;
+ private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
+ @NonNull SparseArray<Intent> idToIntentMapping) {
+ List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
+ for (int i = 0; i < idToIntentMapping.size(); i++) {
+ String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
+ Intent currentIntent = idToIntentMapping.valueAt(i);
+ intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
+ individualSize)
+ .thenAccept(items -> {
+ items.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(currentIntentUri, items);
+ }));
+ }
+
+ return CompletableFuture.allOf(intentFutureList.toArray(CompletableFuture[]::new));
}
private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent) {
+ Intent intent, int individualSize) {
if (intent == null) {
Log.e(LOG_TAG, "Null intent received when generating adapter future");
return CompletableFuture.completedFuture(new RemoteCollectionItems
- .Builder().build());
+ .Builder().build());
}
final Context context = ActivityThread.currentApplication();
- final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
+ final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
result.defaultExecutor(), new ServiceConnection() {
@Override
@@ -1348,11 +1405,11 @@ public class RemoteViews implements Parcelable, Filter {
RemoteCollectionItems items;
try {
items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems();
+ .getRemoteCollectionItems(individualSize);
} catch (RemoteException re) {
items = new RemoteCollectionItems.Builder().build();
- Log.e(LOG_TAG, "Error getting collection items from the factory",
- re);
+ Log.e(LOG_TAG, "Error getting collection items from the"
+ + " factory", re);
} finally {
context.unbindService(this);
}
@@ -1371,10 +1428,17 @@ public class RemoteViews implements Parcelable, Filter {
return result;
}
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(Parcel out, int flags,
+ @Nullable SparseArray<Intent> intentsToIgnore) {
out.writeInt(mIdToUriMapping.size());
for (int i = 0; i < mIdToUriMapping.size(); i++) {
- out.writeInt(mIdToUriMapping.keyAt(i));
+ int currentIntentId = mIdToUriMapping.keyAt(i);
+ if (intentsToIgnore != null && intentsToIgnore.contains(currentIntentId)) {
+ // Skip writing collections that are to be updated in the following steps to
+ // better estimate the RemoteViews size.
+ continue;
+ }
+ out.writeInt(currentIntentId);
String intentUri = mIdToUriMapping.valueAt(i);
out.writeString8(intentUri);
mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
@@ -6724,7 +6788,13 @@ public class RemoteViews implements Parcelable, Filter {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcel(dest, flags, /* intentsToIgnore= */ null);
+ }
+
+ private void writeToParcel(Parcel dest, int flags,
+ @Nullable SparseArray<Intent> intentsToIgnore) {
boolean prevSquashingAllowed = dest.allowSquashing();
if (!hasMultipleLayouts()) {
@@ -6733,7 +6803,7 @@ public class RemoteViews implements Parcelable, Filter {
// is shared by all children.
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
- mCollectionCache.writeToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
}
mApplication.writeToParcel(dest, flags);
if (mIsRoot || mIdealSize == null) {
@@ -6750,7 +6820,7 @@ public class RemoteViews implements Parcelable, Filter {
dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
- mCollectionCache.writeToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
}
dest.writeInt(mSizedRemoteViews.size());
for (RemoteViews view : mSizedRemoteViews) {
@@ -6762,7 +6832,7 @@ public class RemoteViews implements Parcelable, Filter {
// is shared by all children.
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
- mCollectionCache.writeToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
}
mLandscape.writeToParcel(dest, flags);
// Both RemoteViews already share the same package and user
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index a250a867b9de..d4a5bbd79d31 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -19,6 +19,7 @@ package android.widget;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import android.os.Parcel;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -43,13 +44,6 @@ public abstract class RemoteViewsService extends Service {
private static final Object sLock = new Object();
/**
- * Used for determining the maximum number of entries to retrieve from RemoteViewsFactory
- *
- * @hide
- */
- private static final int MAX_NUM_ENTRY = 10;
-
- /**
* An interface for an adapter between a remote collection view (ListView, GridView, etc) and
* the underlying data for that view. The implementor is responsible for making a RemoteView
* for each item in the data set. This interface is a thin wrapper around {@link Adapter}.
@@ -235,9 +229,10 @@ public abstract class RemoteViewsService extends Service {
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
.Builder().build();
+ Parcel capSizeTestParcel = Parcel.obtain();
try {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
@@ -245,15 +240,25 @@ public abstract class RemoteViewsService extends Service {
mFactory.onDataSetChanged();
itemsBuilder.setHasStableIds(mFactory.hasStableIds());
- final int numOfEntries = Math.min(mFactory.getCount(), MAX_NUM_ENTRY);
+ final int numOfEntries = mFactory.getCount();
+
for (int i = 0; i < numOfEntries; i++) {
- itemsBuilder.addItem(mFactory.getItemId(i), mFactory.getViewAt(i));
+ final long currentItemId = mFactory.getItemId(i);
+ final RemoteViews currentView = mFactory.getViewAt(i);
+ currentView.writeToParcel(capSizeTestParcel, 0);
+ if (capSizeTestParcel.dataSize() > capSize) {
+ break;
+ }
+ itemsBuilder.addItem(currentItemId, currentView);
}
items = itemsBuilder.build();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ } finally {
+ // Recycle the parcel
+ capSizeTestParcel.recycle();
}
return items;
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index a1ebde76e98e..42c2d80ea322 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.flags.Flags.viewVelocityApi;
+
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
@@ -726,6 +728,12 @@ public class ScrollView extends FrameLayout {
* isFinished() is correct.
*/
mScroller.computeScrollOffset();
+
+ // For variable refresh rate project to track the current velocity of this View
+ if (viewVelocityApi()) {
+ setFrameContentVelocity(Math.abs(mScroller.getCurrVelocity()));
+ }
+
mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowBottom.isFinished()
|| !mEdgeGlowTop.isFinished();
// Catch the edge effect if it is active.
@@ -1573,6 +1581,11 @@ public class ScrollView extends FrameLayout {
// Keep on drawing until the animation has finished.
postInvalidateOnAnimation();
}
+
+ // For variable refresh rate project to track the current velocity of this View
+ if (viewVelocityApi()) {
+ setFrameContentVelocity(Math.abs(mScroller.getCurrVelocity()));
+ }
} else {
if (mFlingStrictSpan != null) {
mFlingStrictSpan.finish();
@@ -1884,6 +1897,10 @@ public class ScrollView extends FrameLayout {
mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,
Math.max(0, bottom - height), 0, height/2);
+ // For variable refresh rate project to track the current velocity of this View
+ if (viewVelocityApi()) {
+ setFrameContentVelocity(Math.abs(mScroller.getCurrVelocity()));
+ }
if (mFlingStrictSpan == null) {
mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling");
}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 51890ecbecb5..94c72c60ecd0 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -46,5 +46,5 @@ flag {
name: "bal_respect_app_switch_state_when_check_bound_by_foreground_uid"
namespace: "responsible_apis"
description: "Prevent BAL based on it is bound by foreground Uid but the app switch is stopped."
- bug: "171459802"
+ bug: "283801068"
}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 65b59790e327..c7695187e52d 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -172,6 +172,20 @@ public class IntentForwarderActivity extends Activity {
newIntent.prepareToLeaveUser(callingUserId);
final CompletableFuture<ResolveInfo> targetResolveInfoFuture =
mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY, targetUserId);
+
+ if (isPrivateProfile(callingUserId)) {
+ buildAndExecuteForPrivateProfile(intentReceived, className, newIntent, callingUserId,
+ targetUserId);
+ } else {
+ buildAndExecute(targetResolveInfoFuture, intentReceived, className, newIntent,
+ callingUserId,
+ targetUserId, userMessage, managedProfile);
+ }
+ }
+
+ private void buildAndExecute(CompletableFuture<ResolveInfo> targetResolveInfoFuture,
+ Intent intentReceived, String className, Intent newIntent, int callingUserId,
+ int targetUserId, String userMessage, UserInfo managedProfile) {
targetResolveInfoFuture
.thenApplyAsync(targetResolveInfo -> {
if (isResolverActivityResolveInfo(targetResolveInfo)) {
@@ -195,6 +209,23 @@ public class IntentForwarderActivity extends Activity {
}, getApplicationContext().getMainExecutor());
}
+ private void buildAndExecuteForPrivateProfile(
+ Intent intentReceived, String className, Intent newIntent, int callingUserId,
+ int targetUserId) {
+ final CompletableFuture<ResolveInfo> targetResolveInfoFuture =
+ mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY, targetUserId);
+ targetResolveInfoFuture
+ .thenAcceptAsync(targetResolveInfo -> {
+ if (isResolverActivityResolveInfo(targetResolveInfo)) {
+ launchResolverActivityWithCorrectTab(intentReceived, className, newIntent,
+ callingUserId, targetUserId);
+ } else {
+ maybeShowUserConsentMiniResolverPrivate(targetResolveInfo, newIntent,
+ targetUserId);
+ }
+ }, getApplicationContext().getMainExecutor());
+ }
+
private void maybeShowUserConsentMiniResolver(
ResolveInfo target, Intent launchIntent, UserInfo managedProfile) {
if (target == null || isIntentForwarderResolveInfo(target) || !isDeviceProvisioned()) {
@@ -233,24 +264,70 @@ public class IntentForwarderActivity extends Activity {
"Showing user consent for redirection into the managed profile for intent [%s] and "
+ " calling package [%s]",
launchIntent, callingPackage));
- int layoutId = R.layout.miniresolver;
- setContentView(layoutId);
+ PackageManager packageManagerForTargetUser =
+ createContextAsUser(UserHandle.of(targetUserId), /* flags= */ 0)
+ .getPackageManager();
+ buildMiniResolver(target, launchIntent, targetUserId,
+ getOpenInWorkMessage(launchIntent, target.loadLabel(packageManagerForTargetUser)),
+ packageManagerForTargetUser);
- findViewById(R.id.title_container).setElevation(0);
+ View telephonyInfo = findViewById(R.id.miniresolver_info_section);
+
+ // Additional information section is work telephony specific. Therefore, it is only shown
+ // for telephony related intents, when all sim subscriptions are in the work profile.
+ if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent))
+ && devicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType()
+ == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+ telephonyInfo.setVisibility(View.VISIBLE);
+ ((TextView) findViewById(R.id.miniresolver_info_section_text))
+ .setText(getWorkTelephonyInfoSectionMessage(launchIntent));
+ } else {
+ telephonyInfo.setVisibility(View.GONE);
+ }
+ }
+
+ private void maybeShowUserConsentMiniResolverPrivate(
+ ResolveInfo target, Intent launchIntent, int targetUserId) {
+ if (target == null || isIntentForwarderResolveInfo(target)) {
+ finish();
+ return;
+ }
+
+ String callingPackage = getCallingPackage();
+
+ Log.i("IntentForwarderActivity", String.format(
+ "Showing user consent for redirection into the main profile for intent [%s] and "
+ + " calling package [%s]",
+ launchIntent, callingPackage));
PackageManager packageManagerForTargetUser =
createContextAsUser(UserHandle.of(targetUserId), /* flags= */ 0)
.getPackageManager();
+ buildMiniResolver(target, launchIntent, targetUserId,
+ getString(R.string.miniresolver_open_in_personal,
+ target.loadLabel(packageManagerForTargetUser)),
+ packageManagerForTargetUser);
+
+ View telephonyInfo = findViewById(R.id.miniresolver_info_section);
+ telephonyInfo.setVisibility(View.GONE);
+ }
+
+ private void buildMiniResolver(ResolveInfo target, Intent launchIntent, int targetUserId,
+ String resolverTitle, PackageManager pmForTargetUser) {
+ int layoutId = R.layout.miniresolver;
+ setContentView(layoutId);
+
+ findViewById(R.id.title_container).setElevation(0);
ImageView icon = findViewById(R.id.icon);
icon.setImageDrawable(
- getAppIcon(target, launchIntent, targetUserId, packageManagerForTargetUser));
+ getAppIcon(target, launchIntent, targetUserId, pmForTargetUser));
View buttonContainer = findViewById(R.id.button_bar_container);
buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom());
((TextView) findViewById(R.id.open_cross_profile)).setText(
- getOpenInWorkMessage(launchIntent, target.loadLabel(packageManagerForTargetUser)));
+ resolverTitle);
// The mini-resolver's negative button is reused in this flow to cancel the intent
((Button) findViewById(R.id.use_same_profile_browser)).setText(R.string.cancel);
@@ -269,21 +346,6 @@ public class IntentForwarderActivity extends Activity {
targetUserId);
finish();
});
-
-
- View telephonyInfo = findViewById(R.id.miniresolver_info_section);
-
- // Additional information section is work telephony specific. Therefore, it is only shown
- // for telephony related intents, when all sim subscriptions are in the work profile.
- if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent))
- && devicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType()
- == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
- telephonyInfo.setVisibility(View.VISIBLE);
- ((TextView) findViewById(R.id.miniresolver_info_section_text))
- .setText(getWorkTelephonyInfoSectionMessage(launchIntent));
- } else {
- telephonyInfo.setVisibility(View.GONE);
- }
}
private Drawable getAppIcon(
@@ -548,6 +610,18 @@ public class IntentForwarderActivity extends Activity {
}
/**
+ * Returns the private profile for this device or null if there is no private profile.
+ */
+ @Nullable
+ private UserInfo getPrivateProfile() {
+ List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId());
+ for (UserInfo userInfo : relatedUsers) {
+ if (userInfo.isPrivateProfile()) return userInfo;
+ }
+ return null;
+ }
+
+ /**
* Returns the userId of the profile parent or UserHandle.USER_NULL if there is
* no parent.
*/
@@ -577,6 +651,17 @@ public class IntentForwarderActivity extends Activity {
return mMetricsLogger;
}
+ private boolean isPrivateProfile(int userId) {
+ UserInfo privateProfile = getPrivateProfile();
+ return privateSpaceFlagsEnabled() && privateProfile != null
+ && privateProfile.id == userId;
+ }
+
+ private boolean privateSpaceFlagsEnabled() {
+ return android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceIntentRedirection();
+ }
+
@VisibleForTesting
protected Injector createInjector() {
return new InjectorImpl();
diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
index 275904347d2b..b45bc1c46967 100644
--- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -25,28 +25,19 @@ import android.view.inputmethod.ImeTracker;
interface IImeTracker {
/**
- * Called when an IME show request is created.
+ * Called when an IME request is started.
*
* @param tag the logging tag.
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME show request.
- * @param reason the reason why the IME show request was created.
+ * @param uid the uid of the client that started the request.
+ * @param type the type of the request.
+ * @param origin the origin of the request.
* @param fromUser whether this request was created directly from user interaction.
- * @return A new IME tracking token.
- */
- ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason, boolean fromUser);
-
- /**
- * Called when an IME hide request is created.
+ * @param reason the reason for starting the request.
*
- * @param tag the logging tag.
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME hide request.
- * @param reason the reason why the IME hide request was created.
- * @param fromUser whether this request was created directly from user interaction.
- * @return A new IME tracking token.
+ * @return An IME request tracking token.
*/
- ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason, boolean fromUser);
+ ImeTracker.Token onStart(String tag, int uid, int type, int origin, int reason,
+ boolean fromUser);
/**
* Called when the IME request progresses to a further phase.
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index 6abd9e8a8a17..2593b78f5182 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -71,11 +71,11 @@ oneway interface IInputMethod {
void setSessionEnabled(IInputMethodSession session, boolean enabled);
- void showSoftInput(in IBinder showInputToken, in @nullable ImeTracker.Token statsToken,
- int flags, in ResultReceiver resultReceiver);
+ void showSoftInput(in IBinder showInputToken, in ImeTracker.Token statsToken, int flags,
+ in ResultReceiver resultReceiver);
- void hideSoftInput(in IBinder hideInputToken, in @nullable ImeTracker.Token statsToken,
- int flags, in ResultReceiver resultReceiver);
+ void hideSoftInput(in IBinder hideInputToken, in ImeTracker.Token statsToken, int flags,
+ in ResultReceiver resultReceiver);
void updateEditorToolType(int toolType);
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 65a2f4be9901..457b9dd34644 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -35,15 +35,17 @@ oneway interface IInputMethodPrivilegedOperations {
void setInputMethod(String id, in AndroidFuture future /* T=Void */);
void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype,
in AndroidFuture future /* T=Void */);
- void hideMySoftInput(int flags, int reason, in AndroidFuture future /* T=Void */);
- void showMySoftInput(int flags, in AndroidFuture future /* T=Void */);
+ void hideMySoftInput(in ImeTracker.Token statsToken, int flags, int reason,
+ in AndroidFuture future /* T=Void */);
+ void showMySoftInput(in ImeTracker.Token statsToken, int flags, int reason,
+ in AndroidFuture future /* T=Void */);
void updateStatusIconAsync(String packageName, int iconId);
void switchToPreviousInputMethod(in AndroidFuture future /* T=Boolean */);
void switchToNextInputMethod(boolean onlyCurrentIme, in AndroidFuture future /* T=Boolean */);
void shouldOfferSwitchingToNextInputMethod(in AndroidFuture future /* T=Boolean */);
void notifyUserActionAsync();
void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible,
- in @nullable ImeTracker.Token statsToken);
+ in ImeTracker.Token statsToken);
void onStylusHandwritingReady(int requestId, int pid);
void resetStylusHandwriting(int requestId);
void switchKeyboardLayoutAsync(int direction);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 9b7fa2f4f9e9..a0aad31d2e04 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -189,6 +189,8 @@ public final class InputMethodDebug {
*/
public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) {
switch (reason) {
+ case SoftInputShowHideReason.NOT_SET:
+ return "NOT_SET";
case SoftInputShowHideReason.SHOW_SOFT_INPUT:
return "SHOW_SOFT_INPUT";
case SoftInputShowHideReason.ATTACH_NEW_INPUT:
@@ -265,6 +267,36 @@ public final class InputMethodDebug {
return "HIDE_SOFT_INPUT_CLOSE_CURRENT_SESSION";
case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW:
return "HIDE_SOFT_INPUT_FROM_VIEW";
+ case SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT:
+ return "SHOW_SOFT_INPUT_LEGACY_DIRECT";
+ case SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT:
+ return "HIDE_SOFT_INPUT_LEGACY_DIRECT";
+ case SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT:
+ return "SHOW_WINDOW_LEGACY_DIRECT";
+ case SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT:
+ return "HIDE_WINDOW_LEGACY_DIRECT";
+ case SoftInputShowHideReason.RESET_NEW_CONFIGURATION:
+ return "RESET_NEW_CONFIGURATION";
+ case SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY:
+ return "UPDATE_CANDIDATES_VIEW_VISIBILITY";
+ case SoftInputShowHideReason.CONTROLS_CHANGED:
+ return "CONTROLS_CHANGED";
+ case SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED:
+ return "DISPLAY_CONFIGURATION_CHANGED";
+ case SoftInputShowHideReason.DISPLAY_INSETS_CHANGED:
+ return "DISPLAY_INSETS_CHANGED";
+ case SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED:
+ return "DISPLAY_CONTROLS_CHANGED";
+ case SoftInputShowHideReason.UNBIND_CURRENT_METHOD:
+ return "UNBIND_CURRENT_METHOD";
+ case SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED:
+ return "HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED";
+ case SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL:
+ return "HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL";
+ case SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT:
+ return "SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
+ case SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION:
+ return "SHOW_SOFT_INPUT_IMM_DEPRECATION";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 792388dca339..635a227e5862 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -252,20 +252,21 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)}
- *
- * @param reason the reason to hide soft input
+ * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput}
*/
@AnyThread
- public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
- @SoftInputShowHideReason int reason) {
+ public void hideMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
try {
final AndroidFuture<Void> future = new AndroidFuture<>();
- ops.hideMySoftInput(flags, reason, future);
+ ops.hideMySoftInput(statsToken, flags, reason, future);
CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -273,17 +274,21 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)}
+ * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput}
*/
@AnyThread
- public void showMySoftInput(@InputMethodManager.ShowFlags int flags) {
+ public void showMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
try {
final AndroidFuture<Void> future = new AndroidFuture<>();
- ops.showMySoftInput(flags, future);
+ ops.showMySoftInput(statsToken, flags, reason, future);
CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -379,19 +384,19 @@ public final class InputMethodPrivilegedOperations {
* {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder,
* int)}
* @param setVisible {@code true} to set IME visible, else hidden.
- * @param statsToken the token tracking the current IME request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*/
@AnyThread
public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
ImeTracker.forLogging().onFailed(statsToken,
- ImeTracker.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER);
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
return;
}
ImeTracker.forLogging().onProgress(statsToken,
- ImeTracker.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER);
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
try {
ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible, statsToken);
} catch (RemoteException e) {
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 861b8a75f730..da738a01ec39 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -34,6 +34,7 @@ import java.lang.annotation.Retention;
*/
@Retention(SOURCE)
@IntDef(value = {
+ SoftInputShowHideReason.NOT_SET,
SoftInputShowHideReason.SHOW_SOFT_INPUT,
SoftInputShowHideReason.ATTACH_NEW_INPUT,
SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME,
@@ -72,8 +73,26 @@ import java.lang.annotation.Retention;
SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE,
SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION,
SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT,
+ SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT,
+ SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT,
+ SoftInputShowHideReason.RESET_NEW_CONFIGURATION,
+ SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY,
+ SoftInputShowHideReason.CONTROLS_CHANGED,
+ SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED,
+ SoftInputShowHideReason.DISPLAY_INSETS_CHANGED,
+ SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED,
+ SoftInputShowHideReason.UNBIND_CURRENT_METHOD,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION,
})
public @interface SoftInputShowHideReason {
+ /** Default, undefined reason. */
+ int NOT_SET = ImeProtoEnums.REASON_NOT_SET;
+
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
int SHOW_SOFT_INPUT = ImeProtoEnums.REASON_SHOW_SOFT_INPUT;
@@ -291,4 +310,91 @@ public @interface SoftInputShowHideReason {
* Hide soft input when {@link InputMethodManager#hideSoftInputFromView(View, int)} gets called.
*/
int HIDE_SOFT_INPUT_FROM_VIEW = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_FROM_VIEW;
+
+ /**
+ * Show soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService.InputMethodImpl#showSoftInput}.
+ */
+ int SHOW_SOFT_INPUT_LEGACY_DIRECT = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_LEGACY_DIRECT;
+
+ /**
+ * Hide soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService.InputMethodImpl#hideSoftInput}.
+ */
+ int HIDE_SOFT_INPUT_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_LEGACY_DIRECT;
+
+ /**
+ * Show soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService#showWindow}.
+ */
+ int SHOW_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_SHOW_WINDOW_LEGACY_DIRECT;
+
+ /**
+ * Hide soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService#hideWindow}.
+ */
+ int HIDE_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_WINDOW_LEGACY_DIRECT;
+
+ /**
+ * Show / Hide soft input by
+ * {@link android.inputmethodservice.InputMethodService#resetStateForNewConfiguration}.
+ */
+ int RESET_NEW_CONFIGURATION = ImeProtoEnums.REASON_RESET_NEW_CONFIGURATION;
+
+ /**
+ * Show / Hide soft input by
+ * {@link android.inputmethodservice.InputMethodService#updateCandidatesVisibility}.
+ */
+ int UPDATE_CANDIDATES_VIEW_VISIBILITY = ImeProtoEnums.REASON_UPDATE_CANDIDATES_VIEW_VISIBILITY;
+
+ /**
+ * Show / Hide soft input by {@link android.view.InsetsController#onControlsChanged}.
+ */
+ int CONTROLS_CHANGED = ImeProtoEnums.REASON_CONTROLS_CHANGED;
+
+ /**
+ * Show soft input by
+ * {@link com.android.wm.shell.common.DisplayImeController#onDisplayConfigurationChanged}.
+ */
+ int DISPLAY_CONFIGURATION_CHANGED = ImeProtoEnums.REASON_DISPLAY_CONFIGURATION_CHANGED;
+
+ /**
+ * Show soft input by
+ * {@link com.android.wm.shell.common.DisplayImeController.PerDisplay#insetsChanged}.
+ */
+ int DISPLAY_INSETS_CHANGED = ImeProtoEnums.REASON_DISPLAY_INSETS_CHANGED;
+
+ /**
+ * Show / Hide soft input by
+ * {@link com.android.wm.shell.common.DisplayImeController.PerDisplay#insetsControlChanged}.
+ */
+ int DISPLAY_CONTROLS_CHANGED = ImeProtoEnums.REASON_DISPLAY_CONTROLS_CHANGED;
+
+ /** Hide soft input by
+ * {@link com.android.server.inputmethod.InputMethodManagerService#onUnbindCurrentMethodByReset}.
+ */
+ int UNBIND_CURRENT_METHOD = ImeProtoEnums.REASON_UNBIND_CURRENT_METHOD;
+
+ /** Hide soft input by {@link android.view.ImeInsetsSourceConsumer#onAnimationStateChanged}. */
+ int HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED =
+ ImeProtoEnums.REASON_HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED;
+
+ /** Hide soft input when we already have a {@link android.view.InsetsSourceControl} by
+ * {@link android.view.ImeInsetsSourceConsumer#requestHide}.
+ */
+ int HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL =
+ ImeProtoEnums.REASON_HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL;
+
+ /**
+ * Show soft input by
+ * {@link android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int)}.
+ */
+ int SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT =
+ ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT;
+
+ /**
+ * Show soft input by the deprecated
+ * {@link InputMethodManager#showSoftInputFromInputMethod(IBinder, int)}.
+ */
+ int SHOW_SOFT_INPUT_IMM_DEPRECATION = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IMM_DEPRECATION;
}
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 00c482722fa3..95ecd47e3037 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -134,6 +134,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.PublicKey;
@@ -639,9 +640,10 @@ public class ParsingPackageUtils {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
- if (Flags.aslInApkAppMetadataSource()
- && ArrayUtils.contains(assets.list(""), APP_METADATA_FILE_NAME)) {
- pkg.setAppMetadataFileInApk(true);
+ if (Flags.aslInApkAppMetadataSource()) {
+ try (InputStream in = assets.open(APP_METADATA_FILE_NAME)) {
+ pkg.setAppMetadataFileInApk(true);
+ } catch (Exception e) { }
}
return input.success(pkg);
diff --git a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
index d9ac5a9fe47a..30de5468001a 100644
--- a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
@@ -61,7 +61,7 @@ public class LegacyProtoLogImpl implements IProtoLog {
private static final int PER_CHUNK_SIZE = 1024;
private static final String TAG = "ProtoLog";
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
- static final String PROTOLOG_VERSION = "1.0.0";
+ static final String PROTOLOG_VERSION = "2.0.0";
private static final int DEFAULT_PER_CHUNK_SIZE = 0;
private final File mLogFile;
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 78bed9478d84..896538564c2f 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -98,7 +98,7 @@ public class ProtoLogImpl {
*/
public static synchronized IProtoLog getSingleInstance() {
if (sServiceInstance == null) {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
sServiceInstance =
new PerfettoProtoLogImpl(sViewerConfigPath);
} else {
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1f4503a69428..dc3b5a8846cf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -65,12 +65,21 @@ interface IInputMethodManager {
InputMethodSubtype getLastInputMethodSubtype(int userId);
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
- in @nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
+ in ImeTracker.Token statsToken, int flags, int lastClickToolType,
in @nullable ResultReceiver resultReceiver, int reason);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
- in @nullable ImeTracker.Token statsToken, int flags,
+ in ImeTracker.Token statsToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);
+ /**
+ * A test API for CTS to request hiding the current soft input window, with the request origin
+ * on the server side.
+ */
+ @EnforcePermission("TEST_INPUT_METHOD")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.TEST_INPUT_METHOD)")
+ void hideSoftInputFromServerForTest();
+
// TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'editorInfo' is non-null then also does startInput.
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 918d9c029ef5..1d0e97295571 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -39,6 +39,6 @@ interface IRemoteViewsFactory {
boolean hasStableIds();
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean isCreated();
- RemoteViews.RemoteCollectionItems getRemoteCollectionItems();
+ RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index b7cb3926d936..7c9fda5c6631 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -92,7 +92,7 @@ public class WireBuffer {
mIndex = 0;
mStartingIndex = 0;
mSize = 0;
- if (expectedSize > mMaxSize) {
+ if (expectedSize >= mMaxSize) {
resize(expectedSize);
}
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 240028c191bc..76e71380017c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -400,6 +400,7 @@ cc_library_shared_for_libandroid_runtime {
"libbinary_parse",
"libdng_sdk",
"libft2",
+ "libhostgraphics",
"libhwui",
"libimage_type_recognition",
"libjpeg",
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 06d5eb305ff0..83b6afa8f8f6 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -196,14 +196,6 @@ static vector<string> parseCsv(const string& csvString) {
return result;
}
-static vector<string> parseCsv(JNIEnv* env, jstring csvJString) {
- const char* charArray = env->GetStringUTFChars(csvJString, 0);
- string csvString(charArray);
- vector<string> result = parseCsv(csvString);
- env->ReleaseStringUTFChars(csvJString, charArray);
- return result;
-}
-
void LayoutlibLogger(base::LogId, base::LogSeverity severity, const char* tag, const char* file,
unsigned int line, const char* message) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -364,7 +356,7 @@ using namespace android;
// Called right before aborting by LOG_ALWAYS_FATAL. Print the pending exception.
void abort_handler(const char* abort_message) {
- ALOGE("Abort to abort the process...");
+ ALOGE("About to abort the process...");
JNIEnv* env = NULL;
if (javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -395,20 +387,28 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+ // Java system properties that contain LayoutLib config. The initial values in the map
+ // are the default values if the property is not specified.
+ std::unordered_map<std::string, std::string> systemProperties =
+ {{"core_native_classes", ""},
+ {"register_properties_during_load", ""},
+ {"icu.data.path", ""},
+ {"use_bridge_for_logging", ""},
+ {"keyboard_paths", ""}};
+
+ for (auto& [name, defaultValue] : systemProperties) {
+ jstring propertyString =
+ (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+ env->NewStringUTF(name.c_str()),
+ env->NewStringUTF(defaultValue.c_str()));
+ const char* propertyChars = env->GetStringUTFChars(propertyString, 0);
+ systemProperties[name] = string(propertyChars);
+ env->ReleaseStringUTFChars(propertyString, propertyChars);
+ }
// Get the names of classes that need to register their native methods
- auto nativesClassesJString =
- (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
- env->NewStringUTF("core_native_classes"),
- env->NewStringUTF(""));
- vector<string> classesToRegister = parseCsv(env, nativesClassesJString);
-
- jstring registerProperty =
- (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
- env->NewStringUTF(
- "register_properties_during_load"),
- env->NewStringUTF(""));
- const char* registerPropertyString = env->GetStringUTFChars(registerProperty, 0);
- if (strcmp(registerPropertyString, "true") == 0) {
+ vector<string> classesToRegister = parseCsv(systemProperties["core_native_classes"]);
+
+ if (systemProperties["register_properties_during_load"] == "true") {
// Set the system properties first as they could be used in the static initialization of
// other classes
if (register_android_os_SystemProperties(env) < 0) {
@@ -423,35 +423,20 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
env->CallStaticVoidMethod(bridge, setSystemPropertiesMethod);
property_initialize_ro_cpu_abilist();
}
- env->ReleaseStringUTFChars(registerProperty, registerPropertyString);
if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
return JNI_ERR;
}
- // Set the location of ICU data
- auto stringPath = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
- env->NewStringUTF("icu.data.path"),
- env->NewStringUTF(""));
- const char* path = env->GetStringUTFChars(stringPath, 0);
-
- if (strcmp(path, "**n/a**") != 0) {
- bool icuInitialized = init_icu(path);
+ if (!systemProperties["icu.data.path"].empty()) {
+ // Set the location of ICU data
+ bool icuInitialized = init_icu(systemProperties["icu.data.path"].c_str());
if (!icuInitialized) {
- fprintf(stderr, "Failed to initialize ICU\n");
return JNI_ERR;
}
- } else {
- fprintf(stderr, "Skip initializing ICU\n");
}
- env->ReleaseStringUTFChars(stringPath, path);
-
- jstring useJniProperty =
- (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
- env->NewStringUTF("use_bridge_for_logging"),
- env->NewStringUTF(""));
- const char* useJniString = env->GetStringUTFChars(useJniProperty, 0);
- if (strcmp(useJniString, "true") == 0) {
+
+ if (systemProperties["use_bridge_for_logging"] == "true") {
layoutLog = FindClassOrDie(env, "com/android/ide/common/rendering/api/ILayoutLog");
layoutLog = MakeGlobalRefOrDie(env, layoutLog);
logMethodId = GetMethodIDOrDie(env, layoutLog, "logAndroidFramework",
@@ -468,23 +453,16 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
// initialize logging, so ANDROD_LOG_TAGS env variable is respected
android::base::InitLogging(nullptr, android::base::StderrLogger);
}
- env->ReleaseStringUTFChars(useJniProperty, useJniString);
// Use English locale for number format to ensure correct parsing of floats when using strtof
setlocale(LC_NUMERIC, "en_US.UTF-8");
- auto keyboardPathsJString =
- (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
- env->NewStringUTF("keyboard_paths"),
- env->NewStringUTF(""));
- const char* keyboardPathsString = env->GetStringUTFChars(keyboardPathsJString, 0);
- if (strcmp(keyboardPathsString, "**n/a**") != 0) {
- vector<string> keyboardPaths = parseCsv(env, keyboardPathsJString);
+ if (!systemProperties["keyboard_paths"].empty()) {
+ vector<string> keyboardPaths = parseCsv(systemProperties["keyboard_paths"]);
init_keyboard(env, keyboardPaths);
} else {
fprintf(stderr, "Skip initializing keyboard\n");
}
- env->ReleaseStringUTFChars(keyboardPathsJString, keyboardPathsString);
return JNI_VERSION_1_6;
}
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index b39d5cf3842b..23adb8f7e2a3 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -622,6 +622,18 @@ static jlong android_view_MotionEvent_nativeCopy(jlong destNativePtr, jlong sour
return reinterpret_cast<jlong>(destEvent);
}
+static jlong android_view_MotionEvent_nativeSplit(jlong destNativePtr, jlong sourceNativePtr,
+ jint idBits) {
+ MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
+ if (!destEvent) {
+ destEvent = new MotionEvent();
+ }
+ MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
+ destEvent->splitFrom(*sourceEvent, static_cast<std::bitset<MAX_POINTER_ID + 1>>(idBits),
+ InputEvent::nextId());
+ return reinterpret_cast<jlong>(destEvent);
+}
+
static jint android_view_MotionEvent_nativeGetId(jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
return event->getId();
@@ -837,6 +849,7 @@ static const JNINativeMethod gMotionEventMethods[] = {
// --------------- @CriticalNative ------------------
{"nativeCopy", "(JJZ)J", (void*)android_view_MotionEvent_nativeCopy},
+ {"nativeSplit", "(JJI)J", (void*)android_view_MotionEvent_nativeSplit},
{"nativeGetId", "(J)I", (void*)android_view_MotionEvent_nativeGetId},
{"nativeGetDeviceId", "(J)I", (void*)android_view_MotionEvent_nativeGetDeviceId},
{"nativeGetSource", "(J)I", (void*)android_view_MotionEvent_nativeGetSource},
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
index e368c6a98a75..53aa710d57b4 100644
--- a/core/proto/android/hardware/sensorprivacy.proto
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -91,9 +91,7 @@ message SensorPrivacyIndividualEnabledSensorProto {
enum StateType {
ENABLED = 1;
DISABLED = 2;
- AUTO_DRIVER_ASSISTANCE_HELPFUL_APPS = 3;
- AUTO_DRIVER_ASSISTANCE_REQUIRED_APPS = 4;
- AUTO_DRIVER_ASSISTANCE_APPS = 5;
+ ENABLED_EXCEPT_ALLOWLISTED_APPS = 3;
}
// DEPRECATED
diff --git a/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto b/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto
index 5a18d9e627e8..b75d545b1305 100644
--- a/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto
+++ b/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto
@@ -39,7 +39,7 @@ message InputMethodManagerServiceProto {
optional string cur_token = 14;
optional int32 cur_token_display_id = 15;
optional bool system_ready = 16;
- optional int32 last_switch_user_id = 17;
+ reserved 17; // deprecated last_switch_user_id
optional bool have_connection = 18;
optional bool bound_to_method = 19;
optional bool is_interactive = 20;
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index db99e5b53875..9151958e2b94 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -79,11 +79,28 @@ message CombinedVibrationEffectProto {
repeated int32 delays = 2;
}
+// Next Tag: 5
message VibrationAttributesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 usage = 1;
optional int32 audio_usage = 2;
optional int32 flags = 3;
+ optional int32 category = 4;
+}
+
+// Next Tag: 4
+message VibrationParamProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional VibrationScaleParamProto scale = 1;
+ optional int64 create_time = 2;
+ optional bool is_from_request = 3;
+}
+
+// Next Tag: 3
+message VibrationScaleParamProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int32 types_mask = 1;
+ optional float scale = 2;
}
// Next Tag: 9
@@ -132,16 +149,19 @@ message VibrationProto {
}
}
-// Next Tag: 25
+// Next Tag: 29
message VibratorManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 vibrator_ids = 1;
optional VibrationProto current_vibration = 2;
optional bool is_vibrating = 3;
+ optional int32 is_vibrator_controller_registered = 27;
optional VibrationProto current_external_vibration = 4;
optional bool vibrator_under_external_control = 5;
optional bool low_power_mode = 6;
optional bool vibrate_on = 24;
+ optional bool keyboard_vibration_on = 25;
+ optional int32 default_vibration_amplitude = 26;
optional int32 alarm_intensity = 18;
optional int32 alarm_default_intensity = 19;
optional int32 haptic_feedback_intensity = 7;
@@ -158,5 +178,6 @@ message VibratorManagerServiceDumpProto {
repeated VibrationProto previous_notification_vibrations = 14;
repeated VibrationProto previous_alarm_vibrations = 15;
repeated VibrationProto previous_vibrations = 16;
- repeated VibrationProto previous_external_vibrations = 17;
+ repeated VibrationParamProto previous_vibration_params = 28;
+ reserved 17; // prev previous_external_vibrations
} \ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 487b5be9ef5c..52bad21e156d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8621,6 +8621,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.system.virtualmachine.SecretkeeperJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.PruneInstantAppsJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
@@ -8675,7 +8679,7 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
- <service android:name="com.android.server.companion.InactiveAssociationsRemovalService"
+ <service android:name="com.android.server.companion.association.InactiveAssociationsRemovalService"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 6cd2c4e277ea..73877b8cf0c0 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Hervat werkapps?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Hervat"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Noodgeval"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Stel ’n skermslot"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Stel skermslot"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Stel ’n skermslot op dié toestel om jou privaat ruimte te gebruik"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Program is nie beskikbaar nie"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is nie op die oomblik beskikbaar nie."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> is nie beskikbaar nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b485b4bdd5f0..e513a07722ed 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"የሥራ መተግበሪያዎች ከቆሙበት ይቀጥሉ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ከቆመበት ቀጥል"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ድንገተኛ አደጋ"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ማያ ገጽ መቆለፊያን ያቀናብሩ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ማያ ገጽ መቆለፊያውን ያቀናብሩ"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"የግል ቦታዎን ለመጠቀም፣ በዚህ መሣሪያ ላይ ማያ ገጽ መቆለፊያን ያቀናብሩ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"መተግበሪያ አይገኝም"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> አሁን አይገኝም።"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> አይገኝም"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index b12c1154f038..95fa5db05b64 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1990,6 +1990,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"أتريد إعادة تفعيل تطبيقات العمل؟"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"إعادة التفعيل"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"الطوارئ"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ضبط قفل شاشة"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ضبط قفل الشاشة"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"لاستخدام مساحتك الخاصة، يجب ضبط قفل شاشة على هذا الجهاز."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"التطبيق غير متاح"</string>
<string name="app_blocked_message" msgid="542972921087873023">"تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> غير متاح الآن."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"تطبيق <xliff:g id="ACTIVITY">%1$s</xliff:g> غير متاح"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 01705ad6cd68..9fb71492a79e 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"কাম সম্পৰ্কীয় এপ্ আনপজ কৰিবনে?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"আনপজ কৰক"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"জৰুৰীকালীন"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"এটা স্ক্ৰীন লক ছেট কৰক"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"স্ক্ৰীন লক ছেট কৰা"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"আপোনাৰ প্ৰাইভেট স্পেচ ব্যৱহাৰ কৰিবলৈ এই ডিভাইচটোত স্ক্ৰীন লক ছেট কৰক"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"এপ্‌টো উপলব্ধ নহয়"</string>
<string name="app_blocked_message" msgid="542972921087873023">"এই মুহূৰ্তত <xliff:g id="APP_NAME">%1$s</xliff:g> উপলব্ধ নহয়।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> উপলব্ধ নহয়"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 4ed5f5eadd5b..5c3c652bb629 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"İş tətbiqi üzrə pauza bitsin?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Pauzanı bitirin"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Fövqəladə hal"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekran kilidi ayarlayın"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ekran kilidi ayarlayın"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Bu cihazda ekran kilidi ayarlamaqla şəxsi sahədən istifadə edin"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Tətbiq əlçatan deyil"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hazırda əlçatan deyil."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> əlçatan deyil"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index b31dca411edb..bda2a5542088 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Uključiti poslovne aplikacije?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Ponovo aktiviraj"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hitan slučaj"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Podesite zaključavanje ekrana"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Podesi zaključavanje ekrana"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Da biste koristili privatni prostor, podesite zaključavanje ekrana na ovom uređaju"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – nije dostupno"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2bd78e9080c1..38b8e4354445 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Уключыць працоўныя праграмы?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Уключыць"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Экстранны выпадак"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Наладзьце блакіроўку экрана"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Наладзіць блакіроўку экрана"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Каб выкарыстоўваць прыватную прастору, на прыладзе неабходна наладзіць блакіроўку экрана"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Праграма недаступная"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" цяпер недаступная."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недаступна: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index dc1e92ca7037..77933bae46f4 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Отмяна на паузата за служ. прил.?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Отмяна на паузата"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Спешен случай"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Настройте заключване на екрана"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Заключване на екрана"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"За да ползвате частното си пространство, настройте заключване на екрана"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Приложението не е достъпно"</string>
<string name="app_blocked_message" msgid="542972921087873023">"В момента няма достъп до <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> не е налице"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 5dbaad626ab9..b7c7399afbb5 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"অফিসের অ্যাপ আনপজ করতে চান?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"আনপজ করুন"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"জরুরি"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"\'স্ক্রিন লক\' সেট-আপ করুন"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"\'স্ক্রিন লক\' ফিচার সেট করুন"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"নিজের প্রাইভেট স্পেস ব্যবহার করতে এই ডিভাইসে \'স্ক্রিন লক\' সেট করুন"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"অ্যাপ পাওয়া যাচ্ছে না"</string>
<string name="app_blocked_message" msgid="542972921087873023">"এই মুহূর্তে <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপ পাওয়া যাচ্ছে না।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> উপলভ্য নেই"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 838a2db90408..90af6303b179 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Pokrenuti poslovne aplikacije?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Ponovo pokreni"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hitan slučaj"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Postavite zaključavanje ekrana"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Postavite zaključavanje ekrana"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Da koristite privatni prostor, postavite zaklj. ekr. na ur."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Nedostupno: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index feb67c42ccf6..f20d334f4a1f 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Reactivar les apps de treball?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Reactiva"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergència"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Defineix un bloqueig de pantalla"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Defineix un bloqueig de pantalla"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Per utilitzar l\'espai privat, defineix un bloq. de pantalla"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'aplicació no està disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Ara mateix, <xliff:g id="APP_NAME">%1$s</xliff:g> no està disponible."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no està disponible"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 1c0529a71e59..d60389047e08 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Zrušit pozastavení pracovních aplikací?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Zrušit pozastavení"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Stav nouze"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Nastavení zámku obrazovky"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nastavit zámek obrazovky"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Pokud chcete používat soukromý prostor, nastavte na tomto zařízení zámek obrazovky"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikace není k dispozici"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> v tuto chvíli není k dispozici."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> není k dispozici"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 3010dc7ec883..fc73fa71b82d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -522,7 +522,7 @@
<string name="permdesc_vibrate" msgid="8733343234582083721">"Tillader, at appen kan administrere vibratoren."</string>
<string name="permdesc_vibrator_state" msgid="7050024956594170724">"Tillader, at appen bruger vibration."</string>
<string name="permlab_callPhone" msgid="1798582257194643320">"ringe direkte op til telefonnumre"</string>
- <string name="permdesc_callPhone" msgid="7892422187827695656">"Tillader, at appen kan ringe til telefonnumre uden din indgriben. Dette kan resultere i uventede debiteringer eller opkald. Vær opmærksom på, at dette ikke giver appen tilladelse til at ringe til alarmnumre. Skadelige apps kan koste dig penge ved at foretage opkald uden din bekræftelse eller ved at ringe op til operatørkoder, hvilket resulterer i, at indgående opkald automatisk viderestilles til et andet nummer."</string>
+ <string name="permdesc_callPhone" msgid="7892422187827695656">"Tillader, at appen kan ringe til telefonnumre uden din indgriben. Dette kan resultere i uventede opkrævninger eller opkald. Vær opmærksom på, at dette ikke giver appen tilladelse til at ringe til alarmnumre. Skadelige apps kan koste dig penge ved at foretage opkald uden din bekræftelse eller ved at ringe op til operatørkoder, hvilket resulterer i, at indgående opkald automatisk viderestilles til et andet nummer."</string>
<string name="permlab_accessImsCallService" msgid="442192920714863782">"få adgang til chat-opkaldstjeneste"</string>
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Tillader, at appen kan bruge chat-tjenesten til at foretage opkald, uden du gør noget."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"læse telefonens status og identitet"</string>
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Vil du genoptage arbejdsapps?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Genoptag"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nødopkald"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Konfigurer en skærmlås"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Konfigurer skærmlås"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Konfigurer en skærmlås på enheden for at bruge dit private område"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgængelig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgængelig lige nu."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> er ikke understøttet"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 803b96720a01..9faf515f9d44 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -477,9 +477,9 @@
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Ermöglicht der App, die Anrufliste deines Android TV-Geräts zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Du solltest wissen, dass dies von schädlichen Apps genutzt werden kann, um Einträge in der Anrufliste zu löschen oder zu ändern."</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Ermöglicht der App, die Anrufliste deines Telefons zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so die Einträge in der Anrufliste löschen oder sie ändern."</string>
<string name="permlab_bodySensors" msgid="662918578601619569">"Zugriff auf Daten des Körpersensors, etwa Herzfrequenz, wenn in Benutzung"</string>
- <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Ermöglicht der App den Zugriff auf Daten des Körpersensors, etwa solche zur Herzfrequenz, zur Temperatur und zum Blutsauerstoffanteil, während die App in Benutzung ist."</string>
+ <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Ermöglicht der App den Zugriff auf Daten des Körpersensors, etwa solche zur Herzfrequenz, zur Temperatur und zur Sauerstoffsättigung, während die App in Benutzung ist."</string>
<string name="permlab_bodySensors_background" msgid="4912560779957760446">"Zugriff auf Daten des Körpersensors, etwa Herzfrequenz, wenn im Hintergrund"</string>
- <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Ermöglicht der App den Zugriff auf Daten des Körpersensors, etwa solche zur Herzfrequenz, zur Temperatur und zum Blutsauerstoffanteil, während die App im Hintergrund ist."</string>
+ <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Ermöglicht der App den Zugriff auf Daten des Körpersensors, etwa solche zur Herzfrequenz, zur Temperatur und zur Sauerstoffsättigung, während die App im Hintergrund ist."</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"Kalendertermine und Details lesen"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Diese App kann alle auf deinem Tablet gespeicherten Kalendertermine lesen und deine Kalenderdaten teilen oder speichern."</string>
<string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Diese App kann alle auf deinem Android TV-Gerät gespeicherten Kalendertermine lesen und die Kalenderdaten teilen oder speichern."</string>
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Geschäftliche Apps nicht mehr pausieren?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Nicht mehr pausieren"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Notruf"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Displaysperre einrichten"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Displaysperre einrichten"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Richte zur Nutzung des privaten Bereichs auf dem Gerät die Displaysperre ein"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist derzeit nicht verfügbar."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nicht verfügbar"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c030d25ecada..eafaf3257113 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Αναίρ. παύσης εφαρμ. εργασιών;"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Αναίρεση παύσης"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Έκτακτη ανάγκη"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ρυθμίστε ένα κλείδωμα οθόνης"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ρύθμιση κλειδώματος οθόνης"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ορίστε κλείδωμα οθόνης"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Η εφαρμογή δεν είναι διαθέσιμη"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν είναι διαθέσιμη αυτήν τη στιγμή."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> δεν διατίθεται"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index cecdce61f3e2..7dd6a5c668df 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Unpause work apps?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Unpause"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Set a screen lock"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Set screen lock"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"To use your private space, set a screen lock on this device"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> unavailable"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index c3cf40402b42..58c015b5ddd9 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Unpause work apps?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Unpause"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Set a screen lock"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Set screen lock"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"To use your private space, set a screen lock on this device"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> unavailable"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index cda0168b4600..fd0cdd520a8f 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Unpause work apps?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Unpause"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Set a screen lock"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Set screen lock"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"To use your private space, set a screen lock on this device"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> unavailable"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index f979e270448c..3dfadb2d3771 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Unpause work apps?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Unpause"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Set a screen lock"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Set screen lock"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"To use your private space, set a screen lock on this device"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> unavailable"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index e620c35f89ad..6c6f1c98fbaa 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‎Unpause work apps?‎‏‎‎‏‎"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎Unpause‎‏‎‎‏‎"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎Emergency‎‏‎‎‏‎"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎Set a screen lock‎‏‎‎‏‎"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎Set screen lock‎‏‎‎‏‎"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎To use your private space, set a screen lock on this device‎‏‎‎‏‎"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎App is not available‎‏‎‎‏‎"</string>
<string name="app_blocked_message" msgid="542972921087873023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is not available right now.‎‏‎‎‏‎"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="ACTIVITY">%1$s</xliff:g>‎‏‎‎‏‏‏‎ unavailable‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index da500d7d50eb..d7af663c8b98 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -109,7 +109,7 @@
<string name="serviceClassPAD" msgid="6850244583416306321">"PAD"</string>
<string name="roamingText0" msgid="7793257871609854208">"Indicador de roaming activado"</string>
<string name="roamingText1" msgid="5073028598334616445">"Indicador de roaming desactivado"</string>
- <string name="roamingText2" msgid="2834048284153110598">"Indicador de roaming titilando"</string>
+ <string name="roamingText2" msgid="2834048284153110598">"Indicador de roaming parpadeando"</string>
<string name="roamingText3" msgid="831690234035748988">"Fuera del vecindario"</string>
<string name="roamingText4" msgid="2171252529065590728">"Fuera de construcción"</string>
<string name="roamingText5" msgid="4294671587635796641">"Roaming: sistema preferido"</string>
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"¿Reanudar apps de trabajo?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Reanudar"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergencia"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Configura bloqueo de pantalla"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Conf. un bloqueo de pantalla"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar esp. privado, configura un bloqueo de pantalla"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"La app no está disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible en este momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no disponible"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 35cd1ebe784f..02e29c0c0e5a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"¿Reactivar apps de trabajo?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Reactivar"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergencia"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Define un bloqueo de pantalla"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Establecer bloqueo de pantalla"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar el espacio privado, define un bloqueo de pantalla"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"La aplicación no está disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"En estos momentos, <xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no disponible"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 24ee88437930..2fe8731d9cda 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Kas lõpetada töörakenduste peatamine?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Lõpeta peatamine"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hädaolukord"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Seadistage ekraanilukk"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Seadistage ekraanilukk"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Seadistage oma privaatse ruumi jaoks seadmele ekraanilukk"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Rakendus ei ole saadaval"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole praegu saadaval."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ei ole saadaval"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 583f8a995d43..c28191a6d36c 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Laneko aplikazioak berraktibatu?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Berraktibatu"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Larrialdia"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ezarri pantailaren blokeoa"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ezarri pantailaren blokeoa"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Eremu pribatua erabiltzeko, ezarri pantailaren blokeoa gailuan"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikazioa ez dago erabilgarri"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ez dago erabilgarri une honetan."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ez dago erabilgarri"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 52de6dac5f28..7155bf4241e6 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -633,8 +633,8 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"به برنامه اجازه می‌دهد مجموعه عکستان را تغییر دهد."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"خواندن مکان‌ها از مجموعه رسانه شما"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"به برنامه اجازه می‌دهد مکان‌ها را از مجموعه رسانه‌تان بخواند."</string>
- <string name="biometric_app_setting_name" msgid="3339209978734534457">"استفاده از زیست‌سنجشی"</string>
- <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استفاده از زیست‌سنجشی یا قفل صفحه"</string>
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"استفاده از داده‌های زیست‌سنجشی"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استفاده از داده‌های زیست‌سنجشی یا قفل صفحه"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"تأیید کنید این شمایید"</string>
<string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"برای ادامه، از زیست‌سنجشی استفاده کنید"</string>
<string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"برای ادامه، از زیست‌سنجشی یا قفل صفحه استفاده کنید"</string>
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"مکث برنامه‌های کاری لغو شود؟"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"لغو مکث"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"اضطراری"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"قفل صفحه تنظیم کنید"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"تنظیم قفل صفحه"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"برای استفاده از فضای خصوصی، قفل صفحه تنظیم کنید"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"برنامه در دسترس نیست"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال‌حاضر در دسترس نیست."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> دردسترس نیست"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 20d4c14c8c86..11d5604bae58 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Laita työsovellukset päälle?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Laita päälle"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hätätilanne"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Näytön lukituksen asettaminen"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Aseta näytön lukitus"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Edellyttää näytön lukitusta"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Sovellus ei ole käytettävissä"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole nyt käytettävissä."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ei käytettävissä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index b6349427b89c..bec2f1fdcbe3 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Réactiver les applis pros?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Réactiver"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgence"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Config. Verrouillage d\'écran"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Config. Verrouillage d\'écran"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Config. VÉ pour util. Esp. pr."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'application n\'est pas accessible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas accessible pour le moment."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non accessible"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f0ed3d6290b5..d76b1ef70716 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Réactiver les applis pro ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Réactiver"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgence"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Définir verrouillage écran"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Activer verrouillage écran"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Pour utiliser votre espace privé, définissez un verrouillage de l\'écran sur cet appareil"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Application non disponible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas disponible pour le moment."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponible"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index da791af2ec3a..6b7507a65081 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Reactivar apps do traballo?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Reactivar"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emerxencia"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Define un bloqueo de pantalla"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Define un bloqueo de pantalla"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar o espazo privado, define un bloqueo de pantalla"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"A aplicación non está dispoñible"</string>
<string name="app_blocked_message" msgid="542972921087873023">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> non está dispoñible neste momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non está dispoñible"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 8f6affa7a91e..9f4919dfadde 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ઑફિસની થોભાવેલી ઍપ ચાલુ કરીએ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ફરી ચાલુ કરો"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ઇમર્જન્સી"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"સ્ક્રીન લૉક સેટ કરો"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"સ્ક્રીન લૉક સેટ કરો"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"તમારી ખાનગી સ્પેસનો ઉપયોગ કરવા, આ ડિવાઇસ પર સ્ક્રીન લૉક સેટ કરો"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ઍપ ઉપલબ્ધ નથી"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> હાલમાં ઉપલબ્ધ નથી."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ઉપલબ્ધ નથી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 53b39ffed09b..6e91d3a2de8d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"वर्क ऐप्लिकेशन चालू करने हैं?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"चालू करें"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"आपातकालीन कॉल"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"स्क्रीन लॉक सेट करें"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"स्क्रीन लॉक सेट करें"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"प्राइवेट स्पेस के लिए, इस डिवाइस पर स्क्रीन लॉक सेट करें"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> इस समय उपलब्ध नहीं है."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध नहीं है"</string>
@@ -2359,9 +2362,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"इससे साथी ऐप्लिकेशन को बैकग्राउंड में फ़ोरग्राउंड सेवाएं चलाने की अनुमति मिलती है."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"माइक्रोफ़ोन इस्तेमाल किया जा सकता है"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"माइक्रोफ़ोन को ब्लॉक किया गया है"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"कॉन्टेंट डिसप्ले नहीं किया जा सकता"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"स्क्रीन शेयर नहीं की जा सकती"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"कोई दूसरा केबल इस्तेमाल करके फिर से कोशिश करें"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"आपका डिवाइस बहुत गर्म हो गया है. जब तक यह ठंडा नहीं हो जाता, तब तक दूसरे डिवाइस पर इसकी स्क्रीन डिसप्ले नहीं की जा सकती"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"आपका डिवाइस बहुत गर्म हो गया है. जब तक यह ठंडा नहीं हो जाता, तब तक दूसरे डिवाइस पर इसकी स्क्रीन शेयर नहीं की जा सकती"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen की सुविधा चालू है"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g>, कॉन्टेंट दिखाने के लिए दोनों स्क्रीन का इस्तेमाल कर रहा है"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 9af2496fadf1..4682f8477df9 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Pokrenuti poslovne aplikacije?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Ponovno pokreni"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Hitni slučaj"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Postavite zaključavanje zaslona"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Postavi zaključavanje zaslona"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Za upotrebu privatnog prostora postavite zaključavanje zaslona na ovom uređaju"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutačno nije dostupna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – nije dostupno"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 17f80d12a1fe..bde17d0e3052 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Feloldja a munkahelyi appokat?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Szüneteltetés feloldása"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Vészhelyzet"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Állítson be képernyőzárat"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Képernyőzár beállítása"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"A magánterület használatához állítson be képernyőzárat"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Az alkalmazás nem hozzáférhető"</string>
<string name="app_blocked_message" msgid="542972921087873023">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg nem hozzáférhető."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"A(z) <xliff:g id="ACTIVITY">%1$s</xliff:g> nem áll rendelkezése"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 51d0dedbef5a..8caacd3709c2 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Վերսկսե՞լ աշխ. հավելվածները"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Վերսկսել"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Արտակարգ իրավիճակ"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Կարգավորեք էկրանի կողպումը"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Կարգավորել էկրանի կողպումը"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Մասնավոր տարածքն օգտագործելու համար այս սարքում կարգավորեք էկրանի կողպումը"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Հավելվածը հասանելի չէ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն այս պահին հասանելի չէ։"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>՝ անհասանելի է"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 29dc27307f36..2020a75dd578 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Batalkan jeda aplikasi kerja?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Batalkan jeda"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Darurat"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Setel kunci layar"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Setel kunci layar"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Untuk menggunakan ruang pribadi, setel kunci layar di perangkat ini"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikasi tidak tersedia"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia saat ini."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> tidak tersedia"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index ca702fa3e68c..f25a4b430ae7 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Ljúka hléi vinnuforrita?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Ljúka hléi"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Neyðartilvik"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Stilltu skjálás"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Stilltu skjálás"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Stilltu skjálás í tækinu til að nota leynirými"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Forrit er ekki tiltækt"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ekki tiltækt núna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ekki í boði"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 2cb3d3d9826d..06fa36f60e0b 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Riattivare le app di lavoro?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Riattiva"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergenza"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Imposta un blocco schermo"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Imposta il blocco schermo"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Per utilizzare il tuo spazio privato, imposta un blocco schermo sul dispositivo"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"L\'app non è disponibile"</string>
<string name="app_blocked_message" msgid="542972921087873023">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è al momento disponibile."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non disponibile"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 3e7c79e050de..1a609b9aa717 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"להפעיל את האפליקציות לעבודה?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ביטול ההשהיה"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"שיחת חירום"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"הגדרת נעילת מסך"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"הגדרה של נעילת מסך"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"כדי להשתמש במרחב הפרטי יש להגדיר נעילת מסך במכשיר"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"האפליקציה לא זמינה"</string>
<string name="app_blocked_message" msgid="542972921087873023">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא זמינה בשלב זה."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> לא זמינה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 576a495fbdb9..688450e412fb 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"仕事用アプリの停止解除"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"停止解除"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"緊急通報"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"画面ロックの設定"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"画面ロックを設定"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"プライベート スペースには画面ロックの設定が必要です"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"アプリの利用不可"</string>
<string name="app_blocked_message" msgid="542972921087873023">"現在 <xliff:g id="APP_NAME">%1$s</xliff:g> はご利用になれません。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>は利用できません"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 571ae42169c5..a1e489a0dc62 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"კვლავ გააქტიურდეს სამსახურის აპები?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"გააქტიურება"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"საგანგებო სიტუაცია"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ეკრანის დაბლოკვის დაყენება"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ეკრანის დაბლოკვის დაყენება"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"კერძო სივრცის გამოსაყენებლად დააყენეთ ამ მოწყობილობაზე ეკრანის დაბლოკვა"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"აპი მიუწვდომელია"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ამჟამად მიუწვდომელია."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> მიუწვდომელია"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 4f50140b34e6..0bef068286e1 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Жұмыс қолданбаларын қайта қосасыз ба?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Қайта қосу"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Құтқару қызметіне қоңырау шалу"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Экран құлпын орнатыңыз"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Экран құлпын орнату"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Құпия кеңістігіңізді қолдану үшін осы құрылғыда экран құлпын орнатыңыз."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Қолданба қолжетімді емес"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> қазір қолжетімді емес."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> қолжетімсіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 8c33299df473..1f805bfd83d5 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ឈប់ផ្អាកកម្មវិធីការងារឬ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ឈប់ផ្អាក"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ពេលមានអាសន្ន"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"កំណត់​ការចាក់​សោអេក្រង់"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"កំណត់​ការចាក់​សោ​អេក្រង់"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ដើម្បីប្រើលំហឯកជនរបស់អ្នក សូមកំណត់ការចាក់សោអេក្រង់នៅលើឧបករណ៍នេះ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"មិនអាច​ប្រើ​កម្មវិធី​នេះបានទេ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"មិនអាច​ប្រើ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេល​នេះ​បានទេ​។"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"មិនអាចប្រើ <xliff:g id="ACTIVITY">%1$s</xliff:g> បានទេ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index e106f1692a1f..c3fab520a924 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ಕೆಲಸದ ಆ್ಯಪ್ ವಿರಾಮ ರದ್ದುಮಾಡಬೇಕೇ"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ವಿರಾಮವನ್ನು ರದ್ದುಗೊಳಿಸಿ"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ತುರ್ತು"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ನಿಮ್ಮ ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್ ಅನ್ನು ಬಳಸಲು, ಈ ಸಾಧನದಲ್ಲಿ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸೆಟ್ ಮಾಡಿ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ಆ್ಯಪ್ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಇದೀಗ ಲಭ್ಯವಿಲ್ಲ."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ಲಭ್ಯವಿಲ್ಲ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ff480c5428b4..9b7556ab607b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"직장 앱 일시중지를 해제하시겠습니까?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"일시중지 해제"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"긴급 전화"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"화면 잠금 설정"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"화면 잠금 설정"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"비공개 스페이스를 사용하려면 이 기기에 화면 잠금을 설정하세요"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"앱을 사용할 수 없습니다"</string>
<string name="app_blocked_message" msgid="542972921087873023">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> 사용할 수 없음"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index e44689d839cc..71c128d013ef 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Жумуш колдонмолорун иштетесизби?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Иштетүү"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Шашылыш чалуу"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Экран кулпусун коюп алыңыз"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Экран кулпусун коюу"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Жеке мейкиндикти колдонуу үчүн бул түзмөктүн экранын кулпулаңыз"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Колдонмо учурда жеткиликсиз"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> учурда жеткиликсиз"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> жеткиликсиз"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 1f6c1a1594a9..8c19e7b785b3 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ຍົກເລີກການຢຸດຊົ່ວຄາວແອັບບ່ອນເຮັດວຽກບໍ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ຍົກເລີກການຢຸດຊົ່ວຄາວ"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ສຸກເສີນ"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ຕັ້ງການລັອກໜ້າຈໍ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ຕັ້ງການລັອກໜ້າຈໍ"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ເພື່ອໃຊ້ພື້ນທີ່ສ່ວນບຸກຄົນ, ໃຫ້ຕັ້ງລັອກໜ້າຈໍຢູ່ອຸປະກອນນີ້"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ແອັບບໍ່ສາມາດໃຊ້ໄດ້"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ສາມາດໃຊ້ໄດ້ໃນຕອນນີ້."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"ບໍ່ສາມາດໃຊ້ <xliff:g id="ACTIVITY">%1$s</xliff:g> ໄດ້"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 0196351bacb4..bf227d04961e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Atš. darbo progr. pristabd.?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Atšaukti pristabdymą"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Pagalbos tarnyba"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekrano užrako nustatymas"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nustatykite ekrano užraktą"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Jei norite naudoti privačią erdvę, nustatykite ekrano užraktą šiame įrenginyje"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Programa nepasiekiama."</string>
<string name="app_blocked_message" msgid="542972921087873023">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu nepasiekiama."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"„<xliff:g id="ACTIVITY">%1$s</xliff:g>“ nepasiekiama"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 46aae3f62458..a575e2a527bb 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Vai aktivizēt darba lietotnes?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Aktivizēt"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Ārkārtas zvans"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Iestatiet ekrāna bloķēšanu"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Iestatīt ekrāna bloķēšanu"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Lai izmantotu privāto telpu, iestatiet ekrāna bloķēšanu."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Lietotne nav pieejama"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik nav pieejama."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nav pieejams"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 1c85e473fdb9..0ff49ed82943 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Да се актив. работните аплик.?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Прекини ја паузата"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Итен случај"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Поставете заклучување екран"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Поставување заклучување екран"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"За да користите „Приватен простор“, поставете заклучување екран на уредов"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апликацијата не е достапна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> не е достапна во моментов."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> е недостапна"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 9917448c1e0d..2648a3855646 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"വർക്ക് ആപ്പുകൾ പുനരാരംഭിക്കണോ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"പുനരാരംഭിക്കുക"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"അടിയന്തരം"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"സ്‌ക്രീൻ ലോക്ക് സജ്ജീകരിക്കൂ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"സ്‌ക്രീൻ ലോക്ക് സജ്ജീകരിക്കൂ"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"സ്വകാര്യ സ്പേസിന്, ഇതിൽ സ്ക്രീൻ ലോക്ക് സജ്ജീകരിക്കൂ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ആപ്പ് ലഭ്യമല്ല"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ലഭ്യമല്ല"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 48d926ccf4e2..c7f85246cb8f 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Ажлын аппыг үргэлжлүүлэх үү?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Үргэлжлүүлэх"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Яаралтай тусламж"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Дэлгэцийн түгжээ тохируулах"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Дэлгэцийн түгжээ тохируулах"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Хаалттай орон зайгаа ашиглах бол уг төхөөрөмжид дэлгэцийн түгжээ тохируулна уу"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апп боломжгүй байна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> яг одоо боломжгүй байна."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> боломжгүй байна"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 8fb6b0f01960..0c625c7399af 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"वर्क ॲप्स पुन्हा सुरू करायची?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"पुन्हा सुरू करा"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"आणीबाणी"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"स्क्रीन लॉक सेट करा"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"स्क्रीन लॉक सेट करा"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"तुमची खाजगी स्पेस वापरण्यास, या डिव्हाइसवर स्क्रीन लॉक सेट करा"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ॲप उपलब्ध नाही"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता उपलब्ध नाही."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध नाही"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 7112ed389f99..9c81b7900403 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Nyahjeda apl kerja?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Nyahjeda"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Kecemasan"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Tetapkan kunci skrin"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Tetapkan kunci skrin"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Tetapkan kunci skrin pada peranti untuk guna ruang privasi"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Apl tidak tersedia"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia sekarang."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> tidak tersedia"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2c3e7b743afe..2d603bc4abe5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"အလုပ်သုံးအက်ပ် ပြန်ဖွင့်မလား။"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ပြန်ဖွင့်ရန်"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"အရေးပေါ်"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ဖန်သားပြင်လော့ခ် သတ်မှတ်ပါ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ဖန်သားပြင်လော့ခ် သတ်မှတ်ရန်"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"သင့်သီးသန့်နေရာသုံးရန် ဤစက်၌ ဖန်သားပြင်လော့ခ် သတ်မှတ်ပါ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"အက်ပ်ကို မရနိုင်ပါ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ယခု မရနိုင်ပါ။"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> မရနိုင်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 541e1b540570..2ea9d4031b71 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Vil du slå på jobbapper igjen?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Slå på"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nød"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Angi en skjermlås"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Angi skjermlås"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"For å bruke det private området, angi en skjermlås på enheten"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgjengelig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgjengelig for øyeblikket."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> er utilgjengelig"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 8c951e0a9dab..b7996f7938ee 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"कामसम्बन्धी एपहरू अनपज गर्ने हो?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"अनपज गर्नुहोस्"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"आपत्‌कालीन"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"स्क्रिन लक सेटअप गर्नुहोस्"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"स्क्रिन लक सेटअप गर्नुहोस्"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"निजी स्पेस प्रयोग गर्न यो डिभाइसमा स्क्रिन लक सेटअप गर्नुहोस्"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"एप उपलब्ध छैन"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> अहिले उपलब्ध छैन।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध छैन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index bbcc2aa88440..8b60b530677f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Werk-apps hervatten?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Hervatten"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Noodgeval"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Schermvergrendeling instellen"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Schermvergrendeling instellen"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Als je je privéruimte wilt gebruiken, stel je een schermvergrendeling op dit apparaat in"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App is niet beschikbaar"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel niet beschikbaar."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> niet beschikbaar"</string>
@@ -2359,7 +2362,7 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Hiermee kan een bijbehorende app services op de voorgrond vanuit de achtergrond starten."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Microfoon is beschikbaar"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Microfoon is geblokkeerd"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kan niet spiegelen naar scherm"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kan niet mirroren naar scherm"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gebruik een andere kabel en probeer het opnieuw"</string>
<string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Je apparaat is te warm en kan pas naar het scherm mirroren als het is afgekoeld"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index d8bcbd10f5f5..b3e62aaf8d9b 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ୱାର୍କ ଆପ୍ସକୁ ପୁଣି ଚାଲୁ କରିବେ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ଜରୁରୀକାଳୀନ"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ଏକ ସ୍କ୍ରିନ୍ ଲକ୍ ସେଟ୍ କରନ୍ତୁ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ଆପଣଙ୍କ ପ୍ରାଇଭେଟ ସ୍ପେସ ବ୍ୟବହାର କରିବାକୁ ଏହି ଡିଭାଇସରେ ଏକ ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ନାହିଁ।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ଉପଲବ୍ଧ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index b762d6621d92..283813d17e9d 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੋਂ ਰੋਕ ਹਟਾਈਏ?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ਰੋਕ ਹਟਾਓ"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ਐਮਰਜੈਂਸੀ"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਵਰਤਣ ਲਈ, ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 5f3e683d8053..5e0e67020e63 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Cofnąć wstrzymanie aplikacji służbowych?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Cofnij wstrzymanie"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Połączenie alarmowe"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ustaw blokadę ekranu"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ustaw blokadę ekranu"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Aby korzystać z przestrzeni prywatnej, ustaw na tym urządzeniu blokadę ekranu"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – brak dostępu"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 9887bf6d5db6..3ccb86a918cc 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Reativar apps de trabalho?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Reativar"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergência"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Defina um bloqueio de tela"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Definir bloqueio de tela"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar o espaço privado, defina um bloqueio de tela neste dispositivo"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
<string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 9903acb3a4ab..f26eb786865e 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Retomar apps de trabalho?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Retomar"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergência"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Defina um bloqueio de ecrã"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Definir bloqueio de ecrã"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar espaço privado, defina bloqueio de ecrã no disp."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"A app não está disponível"</string>
<string name="app_blocked_message" msgid="542972921087873023">"De momento, a app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 9887bf6d5db6..3ccb86a918cc 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Reativar apps de trabalho?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Reativar"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergência"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Defina um bloqueio de tela"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Definir bloqueio de tela"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar o espaço privado, defina um bloqueio de tela neste dispositivo"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
<string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 73d92fbe6fd6..049ef0ce0bd9 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Reactivezi aplicații de lucru?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Reactivează"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgență"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Setează o blocare a ecranului"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Setează blocarea ecranului"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ca să folosești spațiul privat, setează blocarea ecranului pe acest dispozitiv"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplicația nu este disponibilă"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu este disponibilă momentan."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nu este disponibilă"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 5b434a56f8ec..8abfb65ce224 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Включить рабочие приложения?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Включить"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Экстренный вызов"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Настройте блокировку экрана"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Настроить блокировку экрана"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Чтобы использовать личное пространство, настройте блокировку экрана на этом устройстве."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Приложение недоступно"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" сейчас недоступно."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недоступно: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 5a271180499f..5078ee0f2d1d 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"කාර්ය යෙදුම් විරාම නොකරන්න ද?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"විරාම නොකරන්න"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"හදිසි අවස්ථාව"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"තිර අගුලක් සකසන්න"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"තිර අගුල සකසන්න"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ඔබේ රහසිගත අවකාශය භාවිතා කිරීමට, මෙම උපාංගයේ තිර අගුලක් සකසන්න"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"යෙදුම ලබා ගත නොහැකිය"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> මේ දැන් ලබා ගත නොහැකිය."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> නොතිබේ"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 16a56d3156f1..a10cc4860f70 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Zrušiť pozast. prac. aplikácií?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Zrušiť pozastavenie"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Tiesňová linka"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Nastavte zámku obrazovky"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nastavte zámku obrazovky"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ak chcete používať súkromný priestor, nastavte v tomto zariadení zámku obrazovky"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikácia nie je dostupná"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> nie je teraz dostupná."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nie je k dispozícii"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 79a275ca7089..70eb803b1511 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Želite znova aktivirati delovne aplikacije?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Znova aktiviraj"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nujni primer"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Nastavitev zaklepanja zaslona"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nastavite zaklepanje zaslona"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Če želite uporabljati zasebni prostor, v tej napravi nastavite zaklepanje zaslona"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija ni na voljo"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno ni na voljo."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"»<xliff:g id="ACTIVITY">%1$s</xliff:g>« ni na voljo"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index fc2e71590b9a..61d815de10e7 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Hiq nga pauza apl. e punës?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Hiq nga pauza"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Urgjenca"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Cakto një kyçje ekrani"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Cakto kyçjen e ekranit"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Për të përdorur hapësirën private, cakto një kyçje ekrani në këtë pajisje"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplikacioni nuk ofrohet"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk ofrohet për momentin."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nuk ofrohet"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4f9ce47d4a90..f0c8a209ebd7 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1987,6 +1987,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Укључити пословне апликације?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Поново активирај"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Хитан случај"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Подесите закључавање екрана"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Подеси закључавање екрана"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Да бисте користили приватни простор, подесите закључавање екрана на овом уређају"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Апликација није доступна"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> тренутно није доступна."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – није доступно"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 7f0a2014b5f0..79cdce4cf39b 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1489,7 +1489,7 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Tillåter att en app begär paketborttagning."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"får be om tillstånd att ignorera batterioptimering"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Appen får be om tillstånd att ignorera batterioptimering."</string>
- <string name="permlab_queryAllPackages" msgid="2928450604653281650">"fråga alla paket"</string>
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"sök efter alla paket"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tillåter att en app ser alla installerade paket."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Peka två gånger för zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Det gick inte att lägga till widgeten."</string>
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Vill du återuppta jobbappar?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Återuppta"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Nödsituation"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ställ in ett skärmlås"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ställ in skärmlås"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ställ in ett skärmlås för enheten om du vill använda ditt privata område."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Appen är inte tillgänglig"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> är inte tillgängligt just nu."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> är inte tillgänglig"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 4d004402a6ba..4111f3a12b12 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Je, ungependa kuacha kusitisha programu za kazini?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Acha kusitisha"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Simu za dharura"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Weka mbinu ya kufunga skrini"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Weka mbinu ya kufunga skrini"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ili utumie sehemu ya faragha, weka mbinu ya kufunga skrini kwenye kifaa hiki"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Programu haipatikani"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> haipatikani hivi sasa."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> haipatikani"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index f962728c8b30..3d339239a1f5 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"பணி ஆப்ஸை மீண்டும் இயக்கவா?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"மீண்டும் இயக்கு"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"அவசர அழைப்பு"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"திரைப் பூட்டை அமையுங்கள்"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"திரைப் பூட்டை அமை"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ரகசிய இடத்தைப் பயன்படுத்த, சாதனத்தில் திரைப் பூட்டை அமையுங்கள்"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"இந்த ஆப்ஸ் இப்போது கிடைப்பதில்லை"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இப்போது கிடைப்பதில்லை."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> இல்லை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 30c28129e8c5..5887cd34646a 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"వర్క్ యాప్స్ అన్‌పాజ్ చేయాలా?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"అన్‌పాజ్ చేయండి"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ఎమర్జెన్సీ"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"స్క్రీన్ లాక్‌ను సెట్ చేయండి"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"స్క్రీన్ లాక్‌ను సెట్ చేయండి"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"మీ ప్రైవేట్ స్పేస్‌ను ఉపయోగించడానికి, ఈ పరికరంలో స్క్రీన్ లాక్ సెట్ చేయండి"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"యాప్ అందుబాటులో లేదు"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> అందుబాటులో లేదు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index c92d6139147b..51ff5ae7e662 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ยกเลิกการหยุดแอปงานชั่วคราวไหม"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"ยกเลิกการหยุดชั่วคราว"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ฉุกเฉิน"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ตั้งล็อกหน้าจอ"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ตั้งล็อกหน้าจอ"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"หากต้องการใช้พื้นที่ส่วนตัว ให้ตั้งการล็อกหน้าจอในอุปกรณ์นี้"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"แอปไม่พร้อมใช้งาน"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่พร้อมใช้งานในขณะนี้"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ไม่พร้อมใช้งาน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d9443349d691..43ce6bcd28d3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"I-unpause ang mga work app?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"I-unpause"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Emergency"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Magtakda ng lock ng screen"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Itakda ang lock ng screen"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para gamitin ang iyong pribadong space, magtakda ng lock ng screen sa device na ito."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Hindi available ang app"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Hindi available sa ngayon ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Hindi available ang <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index dfae3016beb3..1df9b8d4d56a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"İş uygulamaları devam ettirilsin mi?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Devam ettir"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Acil durum"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekran kilidi ayarlayın"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ekran kilidi ayarla"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Özel alanı kullanmak için cihazda ekran kilidi ayarlayın"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Uygulama kullanılamıyor"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması şu anda kullanılamıyor."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> kullanılamıyor"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index c8fa2108e6d3..903261c18058 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1988,6 +1988,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Увімкнути робочі додатки?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Увімкнути"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Екстрений виклик"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Налаштуйте блокування екрана"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Налаштувати блокування екрана"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Для доступу до приватного простору налаштуйте блокування екрана"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Додаток недоступний"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> зараз недоступний."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недоступно: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 5ad1f1832077..bed6cf872a45 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"ورک ایپس کو غیر موقوف کریں؟"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"غیر موقوف کریں"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ایمرجنسی"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"اسکرین لاک سیٹ کریں"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"اسکرین لاک سیٹ کریں"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"اپنی نجی اسپیس استعمال کرنے کیلئے، اس آلہ پر اسکرین لاک سیٹ کریں"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> دستیاب نہیں ہے"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index a3ffb9c62ad5..131689883154 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Ishga oid ilovalar qaytarilsinmi?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Davom ettirish"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Favqulodda holat"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekran qulfini sozlash"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ekran qulfini sozlash"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Maxfiy makon ishlatish uchun bu qurilma ekran qulfini sozlang"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Ilova ishlamayapti"</string>
<string name="app_blocked_message" msgid="542972921087873023">"Ayni vaqtda <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi ishlamayapti."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> kanali ish faoliyatida emas"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 8f52be0cb10d..4587a625cb13 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Tiếp tục dùng ứng dụng công việc?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Tiếp tục dùng"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Khẩn cấp"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Đặt phương thức khoá màn hình"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Đặt phương thức khoá màn hình"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Để dùng không gian riêng tư, hãy thiết lập một phương thức khoá màn hình trên thiết bị này"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Ứng dụng này không dùng được"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện không dùng được."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"Không hỗ trợ <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 28f563d2f170..c58c0dee0a05 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"是否为工作应用解除暂停状态?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"解除暂停"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"紧急呼叫"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"设置一种屏锁方式"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"设置屏锁方式"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"若要使用私密空间,请在此设备上设置屏锁方式"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"应用无法使用"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前无法使用。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>不可用"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 59a2abd442eb..b72569a90909 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"要取消暫停工作應用程式嗎?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"取消暫停"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"撥打緊急電話"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"設定螢幕鎖定"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"設定螢幕鎖定"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"如要使用私人空間,請在此裝置上設定螢幕鎖定功能"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"無法使用應用程式"</string>
<string name="app_blocked_message" msgid="542972921087873023">"目前無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"無法使用「<xliff:g id="ACTIVITY">%1$s</xliff:g>」"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 2233668ba404..3c7619c9131d 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"要解除工作應用程式的暫停狀態嗎?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"取消暫停"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"撥打緊急電話"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"設定螢幕鎖定功能"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"設定螢幕鎖定功能"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"如要使用私人空間,請在這部裝置設定螢幕鎖定功能"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"應用程式無法使用"</string>
<string name="app_blocked_message" msgid="542972921087873023">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前無法使用。"</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"無法存取「<xliff:g id="ACTIVITY">%1$s</xliff:g>」"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 176c3743825d..c9dd914de07a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1986,6 +1986,9 @@
<string name="work_mode_off_title" msgid="6367463960165135829">"Susa ukumisa ama-app omsebenzi?"</string>
<string name="work_mode_turn_on" msgid="5316648862401307800">"Qhubekisa"</string>
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Isimo esiphuthumayo"</string>
+ <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Setha ukukhiya isikrini"</string>
+ <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Setha ukukhiya isikrini"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ukuze usebenzise isikhala esigodliwe, setha ukukhiya kwesikrini kule divayisi."</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Uhlelo lokusebenza alutholakali"</string>
<string name="app_blocked_message" msgid="542972921087873023">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayitholakali khona manje."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"okungatholakali <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c882938b63ce..d89f23614179 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -301,7 +301,9 @@
granted to the system companion device manager service -->
<flag name="companion" value="0x800000" />
<!-- Additional flag from base permission type: this permission will be granted to the
- retail demo app, as defined by the OEM. -->
+ retail demo app, as defined by the OEM.
+ This flag has been replaced by the retail demo role and is a no-op since Android V.
+ -->
<flag name="retailDemo" value="0x1000000" />
<!-- Additional flag from base permission type: this permission will be granted to the
recents app. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6134e788be82..4f20fceef8d1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3348,26 +3348,23 @@
<string name="config_carrierAppInstallDialogComponent" translatable="false"
>com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string>
- <!-- Name of the default framework dialog that is used to get or save an app credential.
+ <!-- Name of the fallback CredentialManager dialog that is used to get or save an app
+ credential.
- This UI should be always launch-able and is used as a fallback when an oem replacement activity
- (defined at config_oemCredentialManagerDialogComponent) is undefined / not found. -->
- <string name="config_credentialManagerDialogComponent" translatable="false"
- >com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string>
- <!-- Whether to allow the credential selector activity to be replaced by an activity at
- run-time (restricted to the privileged activity specified by
- config_credentialSelectorActivityName).
+ If empty, no fallback will be used. IMPORTANT: In that case the OEM dialog value specified in
+ config_oemCredentialManagerDialogComponent must always launch-able. Otherwise, the
+ CredentialManager API contract is broken.
- When disabled, the fallback activity defined at
- config_credentialManagerDialogComponent will be used instead. -->
- <bool name="config_enableOemCredentialManagerDialogComponent" translatable="false">true</bool>
+ If specified, this UI should be always launch-able. It will be used as a fallback when the OEM
+ dialog value specified in config_oemCredentialManagerDialogComponent) is undefined / not
+ found. -->
+ <string name="config_fallbackCredentialManagerDialogComponent" translatable="false"
+ >com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string>
<!-- Fully qualified activity name providing the credential selector UI, that serves the
- CredentialManager APIs.
-
- Used only when config_enableOemCredentialManagerDialogComponent is true.
+ CredentialManager APIs. Must be a system app component.
- If the activity specified cannot be found or launched, then the fallback activity defined at
- config_credentialManagerDialogComponent will be used instead. -->
+ If empty, or if this activity specified cannot be found or launched, then the fallback activity
+ defined at config_fallbackCredentialManagerDialogComponent will be used instead. -->
<string name="config_oemCredentialManagerDialogComponent" translatable="false"></string>
<!-- Name of the broadcast receiver that is used to receive provider change events -->
@@ -3554,10 +3551,6 @@
<string name="config_keyguardComponent" translatable="false"
>com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
- <!-- Screen record dialog component -->
- <string name="config_screenRecorderComponent" translatable="false"
- >com.android.systemui/com.android.systemui.screenrecord.ScreenRecordDialog</string>
-
<!-- The component name of a special dock app that merely launches a dream.
We don't want to launch this app when docked because it causes an unnecessary
activity transition. We just want to start the dream. -->
@@ -3614,8 +3607,7 @@
<!-- Whether this device prefers to show snapshot or splash screen on back predict target.
When set true, there will create windowless starting surface for the preview target, so it
won't affect activity's lifecycle. This should only be disabled on low-ram device. -->
- <!-- TODO(b/268563842) enable once activity snapshot is ready -->
- <bool name="config_predictShowStartingSurface">false</bool>
+ <bool name="config_predictShowStartingSurface">true</bool>
<!-- default window ShowCircularMask property -->
<bool name="config_windowShowCircularMask">false</bool>
@@ -4101,6 +4093,13 @@
<!-- How close vibration request should be when they're aggregated for dumpsys, in ms. -->
<integer name="config_previousVibrationsDumpAggregationTimeMillisLimit">1000</integer>
+ <!-- How long history of vibration control service should be kept for the dumpsys. -->
+ <integer name="config_vibratorControlServiceDumpSizeLimit">50</integer>
+
+ <!-- How close requests to vibration control service should be when they're aggregated for
+ dumpsys, in ms. -->
+ <integer name="config_vibratorControlServiceDumpAggregationTimeMillisLimit">60000</integer>
+
<!-- The default vibration strength, must be between 1 and 255 inclusive. -->
<integer name="config_defaultVibrationAmplitude">255</integer>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index c87b7cdb1e8e..6e56fe2abfdd 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -84,7 +84,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">4000</integer>
+ <integer name="auto_data_switch_score_tolerance">-1</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
@@ -334,4 +334,9 @@
<bool name="config_enable_cellular_on_boot_default">true</bool>
<java-symbol type="bool" name="config_enable_cellular_on_boot_default" />
+ <!-- Defines metrics pull cooldown period. The default cooldown period is 23 hours,
+ some Telephony metrics need to be pulled more frequently -->
+ <integer name="config_metrics_pull_cooldown_millis">82800000</integer>
+ <java-symbol type="integer" name="config_metrics_pull_cooldown_millis" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 59066eb83f1c..238772f5961e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6434,4 +6434,6 @@ ul.</string>
<string name="satellite_notification_open_message">Open Messages</string>
<!-- Invoke Satellite setting activity of Settings -->
<string name="satellite_notification_how_it_works">How it works</string>
+ <!-- Initial/System provided label shown for an app which gets unarchived. [CHAR LIMIT=64]. -->
+ <string name="unarchival_session_app_label">Pending...</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2f5183fc1455..06bcd26417e0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -375,7 +375,6 @@
<java-symbol type="string" name="config_recentsComponentName" />
<java-symbol type="string" name="config_systemUIServiceComponent" />
<java-symbol type="string" name="config_controlsPackage" />
- <java-symbol type="string" name="config_screenRecorderComponent" />
<java-symbol type="string" name="config_somnambulatorComponent" />
<java-symbol type="string" name="config_screenshotAppClipsServiceComponent" />
<java-symbol type="string" name="config_screenshotServiceComponent" />
@@ -2083,6 +2082,8 @@
<java-symbol type="integer" name="config_recentVibrationsDumpSizeLimit" />
<java-symbol type="integer" name="config_previousVibrationsDumpSizeLimit" />
<java-symbol type="integer" name="config_previousVibrationsDumpAggregationTimeMillisLimit" />
+ <java-symbol type="integer" name="config_vibratorControlServiceDumpSizeLimit" />
+ <java-symbol type="integer" name="config_vibratorControlServiceDumpAggregationTimeMillisLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
<java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />
@@ -2295,8 +2296,7 @@
<java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
<java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
- <java-symbol type="string" name="config_credentialManagerDialogComponent" />
- <java-symbol type="bool" name="config_enableOemCredentialManagerDialogComponent" />
+ <java-symbol type="string" name="config_fallbackCredentialManagerDialogComponent" />
<java-symbol type="string" name="config_oemCredentialManagerDialogComponent" />
<java-symbol type="string" name="config_credentialManagerReceiverComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
@@ -4364,6 +4364,7 @@
<java-symbol type="dimen" name="seekbar_thumb_exclusion_max_size" />
<java-symbol type="layout" name="chooser_az_label_row" />
<java-symbol type="string" name="chooser_all_apps_button_label" />
+ <java-symbol type="anim" name="resolver_close_anim" />
<java-symbol type="anim" name="resolver_launch_anim" />
<java-symbol type="style" name="Animation.DeviceDefault.Activity.Resolver" />
@@ -5372,4 +5373,6 @@
<java-symbol type="string" name="config_defaultContextualSearchKey" />
<java-symbol type="string" name="config_defaultContextualSearchEnabled" />
<java-symbol type="string" name="config_defaultContextualSearchLegacyEnabled" />
+
+ <java-symbol type="string" name="unarchival_session_app_label" />
</resources>
diff --git a/core/res/res/xml/bookmarks.xml b/core/res/res/xml/bookmarks.xml
index 3087aaaefd41..22d02262c388 100644
--- a/core/res/res/xml/bookmarks.xml
+++ b/core/res/res/xml/bookmarks.xml
@@ -33,7 +33,7 @@
-->
<bookmarks>
<bookmark
- category="android.intent.category.APP_BROWSER"
+ role="android.app.role.BROWSER"
shortcut="b" />
<bookmark
category="android.intent.category.APP_CONTACTS"
@@ -51,7 +51,7 @@
category="android.intent.category.APP_MUSIC"
shortcut="p" />
<bookmark
- category="android.intent.category.APP_MESSAGING"
+ role="android.app.role.SMS"
shortcut="s" />
<bookmark
category="android.intent.category.APP_CALCULATOR"
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 61e6a36839ff..7d740ef76daf 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -88,6 +88,9 @@
<!-- Colombia: 1-6 digits (not confirmed) -->
<shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289" />
+ <!-- Costa Rica -->
+ <shortcode country="cr" pattern="\\d{1,6}" free="466453" />
+
<!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
@@ -143,6 +146,9 @@
<!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece -->
<shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}|12115" />
+ <!-- Guatemala -->
+ <shortcode country="gt" pattern="\\d{1,6}" free="466453" />
+
<!-- Croatia -->
<shortcode country="hr" pattern="\\d{1,5}" free="13062" />
@@ -241,10 +247,10 @@
<shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
<!-- Pakistan -->
- <shortcode country="pk" pattern="\\d{1,5}" free="2057" />
+ <shortcode country="pk" pattern="\\d{1,5}" free="2057|9092" />
<!-- Palestine: 5 digits, known premium codes listed -->
- <shortcode country="ps" pattern="\\d{1,5}" free="37477" />
+ <shortcode country="ps" pattern="\\d{1,5}" free="37477|6681" />
<!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012|80921" />
@@ -324,4 +330,7 @@
<!-- South Africa -->
<shortcode country="za" pattern="\\d{1,5}" free="44136|30791|36056" />
+ <!-- Zimbabwe -->
+ <shortcode country="zw" pattern="\\d{1,5}" free="33679" />
+
</shortcodes>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
index fbb446b5bc23..36a64303cddd 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
@@ -22,8 +22,8 @@ import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.UniqueProgramIdentifier;
-import android.test.suitebuilder.annotation.MediumTest;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
diff --git a/core/tests/ConnectivityManagerTest/Android.bp b/core/tests/ConnectivityManagerTest/Android.bp
index beaf17615f1d..f17a28d22c17 100644
--- a/core/tests/ConnectivityManagerTest/Android.bp
+++ b/core/tests/ConnectivityManagerTest/Android.bp
@@ -27,7 +27,10 @@ android_test {
"android.test.runner",
"android.test.base",
],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "androidx.test.rules",
+ ],
// Include all test java files.
srcs: ["src/**/*.java"],
platform_apis: true,
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
index 2d291ffc70ed..32da1d848183 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -22,7 +22,8 @@ import android.net.NetworkInfo.State;
import android.net.wifi.WifiManager;
import android.os.SystemClock;
import android.provider.Settings;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
index 23135dd15690..9443766e567b 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java
@@ -22,9 +22,9 @@ import android.net.wifi.WifiConfiguration.GroupCipher;
import android.net.wifi.WifiConfiguration.PairwiseCipher;
import android.net.wifi.WifiConfiguration.Protocol;
import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
import android.os.Bundle;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
import com.android.connectivitymanagertest.WifiAssociationTestRunner;
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index b37daa3371f9..9c61647db0c2 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -18,7 +18,8 @@ package com.android.connectivitymanagertest.functional;
import android.net.wifi.WifiConfiguration;
import android.os.SystemClock;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
import com.android.connectivitymanagertest.WifiConfigurationHelper;
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
index 4b82c3debcf5..993b9efdaf06 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -29,9 +29,10 @@ import android.os.Environment;
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner;
import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
import com.android.connectivitymanagertest.WifiConfigurationHelper;
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
index 5c2f388a5dc4..9dc3fced829c 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -17,19 +17,19 @@
package com.android.connectivitymanagertest.unit;
import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.Context;
import android.net.NetworkInfo;
-import android.net.wifi.WifiManager;
+import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.Status;
-import android.net.wifi.SupplicantState;
-
-import android.test.suitebuilder.annotation.LargeTest;
+import android.net.wifi.WifiManager;
import android.test.AndroidTestCase;
+import androidx.test.filters.LargeTest;
+
import java.util.List;
/**
diff --git a/core/tests/bandwidthtests/Android.bp b/core/tests/bandwidthtests/Android.bp
index d0b42f783bef..8645b39da5a8 100644
--- a/core/tests/bandwidthtests/Android.bp
+++ b/core/tests/bandwidthtests/Android.bp
@@ -31,6 +31,9 @@ android_test {
"org.apache.http.legacy",
"android.test.base",
],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "androidx.test.rules",
+ ],
platform_apis: true,
}
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 4b42f4ae9888..b2c85a243836 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -30,9 +30,10 @@ import android.os.Process;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
import com.android.bandwidthtest.util.BandwidthTestUtil;
import com.android.bandwidthtest.util.ConnectionUtil;
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 37a499adf682..6cc54850dce6 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -110,8 +110,6 @@ public class BugreportManagerTest {
Paths.get("/data/misc/wmtrace/ime_trace_service.winscope"),
Paths.get("/data/misc/wmtrace/wm_trace.winscope"),
Paths.get("/data/misc/wmtrace/wm_log.winscope"),
- Paths.get("/data/misc/wmtrace/wm_transition_trace.winscope"),
- Paths.get("/data/misc/wmtrace/shell_transition_trace.winscope"),
};
private Handler mHandler;
@@ -257,6 +255,38 @@ public class BugreportManagerTest {
assertThatAllFileContentsAreDifferent(preDumpedTraceFiles, actualTraceFiles);
}
+ @LargeTest
+ @Test
+ public void noPreDumpData_then_fullWithUsePreDumpFlag_ignoresFlag() throws Exception {
+ startPreDumpedUiTraces();
+
+ mBrm.preDumpUiData();
+ waitTillDumpstateExitedOrTimeout();
+
+ // Simulate lost of pre-dumped data.
+ // For example it can happen in this scenario:
+ // 1. Pre-dump data
+ // 2. Start bugreport + "use pre-dump" flag (USE AND REMOVE THE PRE-DUMP FROM DISK)
+ // 3. Start bugreport + "use pre-dump" flag (NO PRE-DUMP AVAILABLE ON DISK)
+ removeFilesIfNeeded(UI_TRACES_PREDUMPED);
+
+ // Start bugreport with "use predump" flag. Because the pre-dumped data is not available
+ // the flag will be ignored and data will be dumped as in normal flow.
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ mBrm.startBugreport(mBugreportFd, null, fullWithUsePreDumpFlag(), mExecutor,
+ callback);
+ shareConsentDialog(ConsentReply.ALLOW);
+ waitTillDoneOrTimeout(callback);
+
+ stopPreDumpedUiTraces();
+
+ assertThat(callback.isDone()).isTrue();
+ assertThat(mBugreportFile.length()).isGreaterThan(0L);
+ assertFdsAreClosed(mBugreportFd);
+
+ assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
+ }
+
@Test
public void simultaneousBugreportsNotAllowed() throws Exception {
// Start bugreport #1
@@ -506,9 +536,6 @@ public class BugreportManagerTest {
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
"cmd window tracing start"
);
- InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
- "service call SurfaceFlinger 1025 i32 1"
- );
}
private static void stopPreDumpedUiTraces() {
@@ -611,6 +638,14 @@ public class BugreportManagerTest {
return files;
}
+ private static void removeFilesIfNeeded(Path[] paths) throws Exception {
+ for (Path path : paths) {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "rm -f " + path.toString()
+ );
+ }
+ }
+
private static ParcelFileDescriptor parcelFd(File file) throws Exception {
return ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index e72beee71e50..24031cad0a3e 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -101,6 +101,7 @@ android_test {
"flickerlib-trace_processor_shell",
"mockito-target-extended-minus-junit4",
"TestParameterInjector",
+ "android.content.res.flags-aconfig-java",
],
libs: [
diff --git a/core/tests/coretests/res/layout/activity_horizontal_scroll_view.xml b/core/tests/coretests/res/layout/activity_horizontal_scroll_view.xml
index 866e1a95c3f5..502921263462 100644
--- a/core/tests/coretests/res/layout/activity_horizontal_scroll_view.xml
+++ b/core/tests/coretests/res/layout/activity_horizontal_scroll_view.xml
@@ -14,105 +14,150 @@
~ limitations under the License
-->
-<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/horizontal_scroll_view">
+ android:orientation="vertical">
- <LinearLayout
+ <HorizontalScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <View
- android:background="#F00"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#880"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#0F0"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#088"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#00F"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#808"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#F00"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#880"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#0F0"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#088"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#00F"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#808"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#F00"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#880"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#0F0"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#088"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#00F"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#808"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- </LinearLayout>
-</HorizontalScrollView>
+ android:layout_height="match_parent"
+ android:id="@+id/horizontal_scroll_view">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <View
+ android:background="#F00"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#880"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#0F0"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#088"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#00F"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#808"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#F00"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#880"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#0F0"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#088"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#00F"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#808"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#F00"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#880"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#0F0"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#088"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#00F"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#808"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ </LinearLayout>
+ </HorizontalScrollView>
+
+ <view
+ class="android.widget.HorizontalScrollViewFunctionalTest$MyHorizontalScrollView"
+ android:id="@+id/my_horizontal_scroll_view"
+ android:layout_width="90dp"
+ android:layout_height="90dp"
+ android:background="#FFF"
+ android:defaultFocusHighlightEnabled="false">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <View
+ android:background="#00F"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#0FF"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#0F0"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#FF0"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#F00"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#F0F"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ </LinearLayout>
+ </view>
+</LinearLayout> \ No newline at end of file
diff --git a/core/tests/coretests/res/layout/activity_scroll_view.xml b/core/tests/coretests/res/layout/activity_scroll_view.xml
index 61fabf8ee437..db8cd026e71a 100644
--- a/core/tests/coretests/res/layout/activity_scroll_view.xml
+++ b/core/tests/coretests/res/layout/activity_scroll_view.xml
@@ -14,105 +14,150 @@
~ limitations under the License
-->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/scroll_view">
+ android:orientation="vertical">
- <LinearLayout
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <View
- android:background="#F00"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#880"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#0F0"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#088"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#00F"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#808"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#F00"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#880"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#0F0"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#088"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#00F"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#808"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#F00"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#880"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#0F0"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#088"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#00F"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- <View
- android:background="#808"
- android:layout_width="100dp"
- android:layout_height="100dp" />
-
- </LinearLayout>
-</ScrollView>
+ android:layout_height="match_parent"
+ android:id="@+id/scroll_view">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <View
+ android:background="#F00"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#880"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#0F0"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#088"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#00F"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#808"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#F00"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#880"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#0F0"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#088"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#00F"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#808"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#F00"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#880"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#0F0"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#088"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#00F"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ <View
+ android:background="#808"
+ android:layout_width="100dp"
+ android:layout_height="100dp" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <view
+ class="android.widget.ScrollViewFunctionalTest$MyScrollView"
+ android:id="@+id/my_scroll_view"
+ android:layout_width="90dp"
+ android:layout_height="90dp"
+ android:background="#FFF"
+ android:defaultFocusHighlightEnabled="false">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <View
+ android:background="#00F"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#0FF"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#0F0"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#FF0"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#F00"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ <View
+ android:background="#F0F"
+ android:layout_width="90dp"
+ android:layout_height="50dp"/>
+ </LinearLayout>
+ </view>
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
index aaa199d4c814..e8b295bd5fdb 100644
--- a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.hardware.usb.UsbDevice;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -58,6 +59,7 @@ public class BrailleDisplayControllerImplTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ private AccessibilityService mAccessibilityService;
private BrailleDisplayController mBrailleDisplayController;
@Mock
@@ -76,12 +78,13 @@ public class BrailleDisplayControllerImplTest {
@Before
public void test() {
MockitoAnnotations.initMocks(this);
- AccessibilityService accessibilityService = spy(new TestAccessibilityService());
- doReturn((Executor) Runnable::run).when(accessibilityService).getMainExecutor();
- doReturn(TEST_SERVICE_CONNECTION_ID).when(accessibilityService).getConnectionId();
+ mAccessibilityService = spy(new TestAccessibilityService());
+ doReturn((Executor) Runnable::run).when(mAccessibilityService).getMainExecutor();
+ doReturn(TEST_SERVICE_CONNECTION_ID).when(mAccessibilityService).getConnectionId();
AccessibilityInteractionClient.addConnection(TEST_SERVICE_CONNECTION_ID,
mAccessibilityServiceConnection, /*initializeCache=*/false);
- mBrailleDisplayController = accessibilityService.getBrailleDisplayController();
+ mBrailleDisplayController = new BrailleDisplayControllerImpl(
+ mAccessibilityService, new Object(), /*isHidrawSupported=*/true);
}
// Automated CTS tests only use the BluetoothDevice version of BrailleDisplayController#connect
@@ -104,4 +107,17 @@ public class BrailleDisplayControllerImplTest {
assertThrows(IllegalStateException.class,
() -> mBrailleDisplayController.connect(usbDevice, mBrailleDisplayCallback));
}
+
+ @Test
+ public void connect_HidrawNotSupported_callsOnConnectionFailed() {
+ BrailleDisplayController controller = new BrailleDisplayControllerImpl(
+ mAccessibilityService, new Object(), /*isHidrawSupported=*/false);
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+
+ controller.connect(usbDevice, mBrailleDisplayCallback);
+
+ verify(mBrailleDisplayCallback).onConnectionFailed(
+ BrailleDisplayController.BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS);
+ verifyZeroInteractions(mAccessibilityServiceConnection);
+ }
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 64c17bdfa731..927c67cb1d36 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -21,16 +21,22 @@ import static android.content.Intent.ACTION_EDIT;
import static android.content.Intent.ACTION_VIEW;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.window.flags.Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
@@ -46,6 +52,7 @@ import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.app.servertransaction.ConfigurationChangeItem;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
@@ -60,7 +67,9 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.view.Display;
@@ -81,11 +90,14 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -103,11 +115,17 @@ public class ActivityThreadTest {
// few sequence numbers the framework used to launch the test activity.
private static final int BASE_SEQ = 10000000;
- @Rule
+ @Rule(order = 0)
public final ActivityTestRule<TestActivity> mActivityTestRule =
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
+ @Rule(order = 1)
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
+ @Mock
+ private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
+
private WindowTokenClientController mOriginalWindowTokenClientController;
private Configuration mOriginalAppConfig;
@@ -115,6 +133,8 @@ public class ActivityThreadTest {
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
// Keep track of the original controller, so that it can be used to restore in tearDown()
// when there is override in some test cases.
mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
@@ -129,6 +149,8 @@ public class ActivityThreadTest {
mCreatedVirtualDisplays = null;
}
WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
+ ClientTransactionListenerController.getInstance()
+ .unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
() -> restoreConfig(ActivityThread.currentActivityThread(), mOriginalAppConfig));
}
@@ -247,7 +269,7 @@ public class ActivityThreadTest {
newConfig.smallestScreenWidthDp++;
transaction = newTransaction(activityThread);
transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), newConfig));
+ activity.getActivityToken(), newConfig, new ActivityWindowInfo()));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -455,11 +477,11 @@ public class ActivityThreadTest {
transaction = newTransaction(activityThread);
transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), activityConfigLandscape));
+ activity.getActivityToken(), activityConfigLandscape, new ActivityWindowInfo()));
transaction.addTransactionItem(ConfigurationChangeItem.obtain(
processConfigPortrait, DEVICE_ID_INVALID));
transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), activityConfigPortrait));
+ activity.getActivityToken(), activityConfigPortrait, new ActivityWindowInfo()));
appThread.scheduleTransaction(transaction);
activity.mTestLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
@@ -783,6 +805,101 @@ public class ActivityThreadTest {
verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken);
}
+ @Test
+ public void testActivityWindowInfoChanged_activityLaunch() {
+ mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
+ mActivityWindowInfoListener);
+
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity);
+
+ verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
+ activityClientRecord.getActivityWindowInfo());
+ }
+
+ @Test
+ public void testActivityWindowInfoChanged_activityRelaunch() throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
+ mActivityWindowInfoListener);
+
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+ appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity);
+
+ // The same ActivityWindowInfo won't trigger duplicated callback.
+ verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
+ activityClientRecord.getActivityWindowInfo());
+
+ final Configuration currentConfig = activity.getResources().getConfiguration();
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000),
+ new Rect(0, 0, 1000, 1000));
+ final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain(
+ activity.getActivityToken(), null, null, 0,
+ new MergedConfiguration(currentConfig, currentConfig),
+ false /* preserveWindow */, activityWindowInfo);
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.addTransactionItem(relaunchItem);
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
+ activityWindowInfo);
+ }
+
+ @Test
+ public void testActivityWindowInfoChanged_activityConfigurationChanged()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
+ mActivityWindowInfoListener);
+
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ clearInvocations(mActivityWindowInfoListener);
+ final Configuration config = new Configuration(activity.getResources().getConfiguration());
+ config.seq++;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ final Rect taskFragmentBounds = new Rect(0, 0, 1000, 1000);
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, taskBounds, taskFragmentBounds);
+ final ActivityConfigurationChangeItem activityConfigurationChangeItem =
+ ActivityConfigurationChangeItem.obtain(
+ activity.getActivityToken(), config, activityWindowInfo);
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.addTransactionItem(activityConfigurationChangeItem);
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mActivityWindowInfoListener).accept(activity.getActivityToken(),
+ activityWindowInfo);
+
+ clearInvocations(mActivityWindowInfoListener);
+ final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo();
+ activityWindowInfo2.set(true /* isEmbedded */, taskBounds, taskFragmentBounds);
+ config.seq++;
+ final ActivityConfigurationChangeItem activityConfigurationChangeItem2 =
+ ActivityConfigurationChangeItem.obtain(
+ activity.getActivityToken(), config, activityWindowInfo2);
+ final ClientTransaction transaction2 = newTransaction(activity);
+ transaction2.addTransactionItem(activityConfigurationChangeItem2);
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ // The same ActivityWindowInfo won't trigger duplicated callback.
+ verify(mActivityWindowInfoListener, never()).accept(any(), any());
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int, ActivityWindowInfo)} to try to push activity configuration to the
@@ -871,7 +988,7 @@ public class ActivityThreadTest {
@NonNull
private static ClientTransaction newStopTransaction(@NonNull Activity activity) {
final StopActivityItem stopStateRequest = StopActivityItem.obtain(
- activity.getActivityToken(), 0 /* configChanges */);
+ activity.getActivityToken());
final ClientTransaction transaction = newTransaction(activity);
transaction.addTransactionItem(stopStateRequest);
@@ -883,7 +1000,7 @@ public class ActivityThreadTest {
private static ClientTransaction newActivityConfigTransaction(@NonNull Activity activity,
@NonNull Configuration config) {
final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), config);
+ activity.getActivityToken(), config, new ActivityWindowInfo());
final ClientTransaction transaction = newTransaction(activity);
transaction.addTransactionItem(item);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index 85a1b4ee3ebd..990739745f24 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -107,7 +107,7 @@ public class ClientTransactionItemTest {
@Test
public void testActivityConfigurationChangeItem_getContextToUpdate() {
final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
- .obtain(mActivityToken, mConfiguration);
+ .obtain(mActivityToken, mConfiguration, new ActivityWindowInfo());
final Context context = item.getContextToUpdate(mHandler);
assertEquals(mActivity, context);
@@ -136,7 +136,7 @@ public class ClientTransactionItemTest {
@Test
public void testDestroyActivityItem_preExecute() {
final DestroyActivityItem item = DestroyActivityItem
- .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ .obtain(mActivityToken, false /* finished */);
item.preExecute(mHandler);
assertEquals(1, mActivitiesToBeDestroyed.size());
@@ -146,7 +146,7 @@ public class ClientTransactionItemTest {
@Test
public void testDestroyActivityItem_postExecute() {
final DestroyActivityItem item = DestroyActivityItem
- .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ .obtain(mActivityToken, false /* finished */);
item.preExecute(mHandler);
item.postExecute(mHandler, mPendingActions);
@@ -156,11 +156,11 @@ public class ClientTransactionItemTest {
@Test
public void testDestroyActivityItem_execute() {
final DestroyActivityItem item = DestroyActivityItem
- .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ .obtain(mActivityToken, false /* finished */);
item.execute(mHandler, mActivityClientRecord, mPendingActions);
verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
- eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
+ eq(false) /* getNonConfigInstance */, any());
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 213fd7bd494d..77d31a5f27e7 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -22,21 +22,29 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.IDisplayManager;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.DisplayInfo;
+import android.window.ActivityWindowInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -44,6 +52,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.function.BiConsumer;
+
/**
* Tests for {@link ClientTransactionListenerController}.
*
@@ -62,6 +72,10 @@ public class ClientTransactionListenerControllerTest {
private IDisplayManager mIDisplayManager;
@Mock
private DisplayManager.DisplayListener mListener;
+ @Mock
+ private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
+ @Mock
+ private IBinder mActivityToken;
private DisplayManagerGlobal mDisplayManager;
private Handler mHandler;
@@ -91,4 +105,24 @@ public class ClientTransactionListenerControllerTest {
verify(mListener).onDisplayChanged(123);
}
+
+ @Test
+ public void testActivityWindowInfoChangedListener() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ mController.registerActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000),
+ new Rect(0, 0, 1000, 1000));
+ mController.onActivityWindowInfoChanged(mActivityToken, activityWindowInfo);
+
+ verify(mActivityWindowInfoListener).accept(mActivityToken, activityWindowInfo);
+
+ clearInvocations(mActivityWindowInfoListener);
+ mController.unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+
+ mController.onActivityWindowInfoChanged(mActivityToken, activityWindowInfo);
+
+ verify(mActivityWindowInfoListener, never()).accept(any(), any());
+ }
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 906558f7603b..584fe16d00ac 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -82,7 +82,8 @@ public class ObjectPoolTests {
@Test
public void testRecycleActivityConfigurationChangeItem() {
- testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config()));
+ testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config(),
+ new ActivityWindowInfo()));
}
@Test
@@ -97,7 +98,7 @@ public class ObjectPoolTests {
@Test
public void testRecycleDestroyActivityItem() {
- testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true, 117));
+ testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true));
}
@Test
@@ -168,7 +169,7 @@ public class ObjectPoolTests {
@Test
public void testRecyclePauseActivityItemItem() {
- testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, 5, true, true));
+ testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, true, true));
}
@Test
@@ -184,7 +185,7 @@ public class ObjectPoolTests {
@Test
public void testRecycleStopItem() {
- testRecycle(() -> StopActivityItem.obtain(mActivityToken, 4));
+ testRecycle(() -> StopActivityItem.obtain(mActivityToken));
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index adb6f2a23847..935bc7565986 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -306,7 +306,7 @@ public class TransactionExecutorTests {
final IBinder token = mock(IBinder.class);
final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
destroyTransaction.addTransactionItem(
- DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
+ DestroyActivityItem.obtain(token, false /* finished */));
destroyTransaction.preExecute(mTransactionHandler);
// The activity should be added to to-be-destroyed container.
assertEquals(1, activitiesToBeDestroyed.size());
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index dbb090fe795b..d451fe58e8e4 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -95,8 +95,11 @@ public class TransactionParcelTests {
@Test
public void testActivityConfigChange() {
// Write to parcel
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
+ new Rect(0, 0, 500, 500));
ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
+ mActivityToken, config(), activityWindowInfo);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -152,8 +155,7 @@ public class TransactionParcelTests {
@Test
public void testDestroy() {
- DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true /* finished */,
- 135 /* configChanges */);
+ DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true /* finished */);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -241,8 +243,7 @@ public class TransactionParcelTests {
public void testPause() {
// Write to parcel
PauseActivityItem item = PauseActivityItem.obtain(mActivityToken, true /* finished */,
- true /* userLeaving */, 135 /* configChanges */, true /* dontReport */,
- true /* autoEnteringPip */);
+ true /* userLeaving */, true /* dontReport */, true /* autoEnteringPip */);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -269,7 +270,7 @@ public class TransactionParcelTests {
@Test
public void testStop() {
// Write to parcel
- StopActivityItem item = StopActivityItem.obtain(mActivityToken, 14 /* configChanges */);
+ StopActivityItem item = StopActivityItem.obtain(mActivityToken);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -300,10 +301,9 @@ public class TransactionParcelTests {
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
+ mActivityToken, config(), new ActivityWindowInfo());
- StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
- 78 /* configChanges */);
+ StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken);
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addTransactionItem(callback1);
@@ -327,7 +327,7 @@ public class TransactionParcelTests {
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
+ mActivityToken, config(), new ActivityWindowInfo());
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addTransactionItem(callback1);
@@ -348,8 +348,7 @@ public class TransactionParcelTests {
mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
// Write to parcel
- StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
- 78 /* configChanges */);
+ StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken);
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addTransactionItem(lifecycleRequest);
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
index 9dce899aa92d..da40f2adec6f 100644
--- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -29,8 +29,8 @@ import android.app.usage.UsageEvents.Event;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.PersistableBundle;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index fae714842b9b..3618543e3ae3 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -18,8 +18,7 @@ package android.app.usage;
import static junit.framework.Assert.fail;
-import android.test.suitebuilder.annotation.SmallTest;
-
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 4a9cb7180a3f..0c1e8793bfc9 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -18,34 +18,52 @@ package android.content.res;
import android.annotation.NonNull;
import android.app.ResourcesManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.LocaleList;
import android.platform.test.annotations.Postsubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Display;
import android.view.DisplayAdjustments;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Postsubmit
+@RunWith(AndroidJUnit4.class)
public class ResourcesManagerTest extends TestCase {
private static final int SECONDARY_DISPLAY_ID = 1;
private static final String APP_ONE_RES_DIR = "app_one.apk";
private static final String APP_ONE_RES_SPLIT_DIR = "app_one_split.apk";
private static final String APP_TWO_RES_DIR = "app_two.apk";
private static final String LIB_RES_DIR = "lib.apk";
+ private static final String TEST_LIB = "com.android.frameworks.coretests.bdr_helper_app1";
private ResourcesManager mResourcesManager;
private Map<Integer, DisplayMetrics> mDisplayMetricsMap;
+ private PackageManager mPackageManager;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
super.setUp();
mDisplayMetricsMap = new HashMap<>();
@@ -93,8 +111,14 @@ public class ResourcesManagerTest extends TestCase {
return mDisplayMetricsMap.get(displayId);
}
};
+
+ mPackageManager = InstrumentationRegistry.getContext().getPackageManager();
}
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Test
@SmallTest
public void testMultipleCallsWithIdenticalParametersCacheReference() {
Resources resources = mResourcesManager.getResources(
@@ -109,6 +133,7 @@ public class ResourcesManagerTest extends TestCase {
assertSame(resources.getImpl(), newResources.getImpl());
}
+ @Test
@SmallTest
public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() {
Resources resources = mResourcesManager.getResources(
@@ -125,6 +150,7 @@ public class ResourcesManagerTest extends TestCase {
assertNotSame(resources, newResources);
}
+ @Test
@SmallTest
public void testAddingASplitCreatesANewImpl() {
Resources resources1 = mResourcesManager.getResources(
@@ -142,6 +168,7 @@ public class ResourcesManagerTest extends TestCase {
assertNotSame(resources1.getImpl(), resources2.getImpl());
}
+ @Test
@SmallTest
public void testUpdateConfigurationUpdatesAllAssetManagers() {
Resources resources1 = mResourcesManager.getResources(
@@ -187,6 +214,7 @@ public class ResourcesManagerTest extends TestCase {
assertEquals(expectedConfig, resources3.getConfiguration());
}
+ @Test
@SmallTest
public void testTwoActivitiesWithIdenticalParametersShareImpl() {
Binder activity1 = new Binder();
@@ -208,6 +236,7 @@ public class ResourcesManagerTest extends TestCase {
assertSame(resources1.getImpl(), resources2.getImpl());
}
+ @Test
@SmallTest
public void testThemesGetUpdatedWithNewImpl() {
Binder activity1 = new Binder();
@@ -237,6 +266,7 @@ public class ResourcesManagerTest extends TestCase {
assertTrue(value.data != 0);
}
+ @Test
@SmallTest
public void testMultipleResourcesForOneActivityGetUpdatedWhenActivityBaseUpdates() {
Binder activity1 = new Binder();
@@ -286,6 +316,7 @@ public class ResourcesManagerTest extends TestCase {
assertEquals(expectedConfig2, resources2.getConfiguration());
}
+ @Test
@SmallTest
public void testChangingActivityDisplayDoesntOverrideDisplayRequestedByResources() {
Binder activity = new Binder();
@@ -322,4 +353,101 @@ public class ResourcesManagerTest extends TestCase {
assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
defaultDisplayResources.getDisplayMetrics().widthPixels);
}
+
+ @Test
+ @SmallTest
+ @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
+ public void testExistingResourcesAfterResourcePathsRegistration()
+ throws PackageManager.NameNotFoundException {
+ // Inject ResourcesManager instance from this test to the ResourcesManager class so that all
+ // the static method can interact with this test smoothly.
+ ResourcesManager oriResourcesManager = ResourcesManager.getInstance();
+ ResourcesManager.setInstance(mResourcesManager);
+
+ // Create a Resources before register resources' paths for a package.
+ Resources resources = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+ assertNotNull(resources);
+ ResourcesImpl oriResImpl = resources.getImpl();
+
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0);
+ Resources.registerResourcePaths(TEST_LIB, appInfo);
+
+ assertNotSame(oriResImpl, resources.getImpl());
+
+ String[] resourcePaths = appInfo.getAllApkPaths();
+ resourcePaths = removeDuplicates(resourcePaths);
+ ApkAssets[] loadedAssets = resources.getAssets().getApkAssets();
+ assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
+
+ // Package resources' paths should be cached in ResourcesManager.
+ assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
+ .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
+
+ // Revert the ResourcesManager instance back.
+ ResourcesManager.setInstance(oriResourcesManager);
+ }
+
+ @Test
+ @SmallTest
+ @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
+ public void testNewResourcesAfterResourcePathsRegistration()
+ throws PackageManager.NameNotFoundException {
+ // Inject ResourcesManager instance from this test to the ResourcesManager class so that all
+ // the static method can interact with this test smoothly.
+ ResourcesManager oriResourcesManager = ResourcesManager.getInstance();
+ ResourcesManager.setInstance(mResourcesManager);
+
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0);
+ Resources.registerResourcePaths(TEST_LIB, appInfo);
+
+ // Create a Resources after register resources' paths for a package.
+ Resources resources = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+ assertNotNull(resources);
+
+ String[] resourcePaths = appInfo.getAllApkPaths();
+ resourcePaths = removeDuplicates(resourcePaths);
+ ApkAssets[] loadedAssets = resources.getAssets().getApkAssets();
+ assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
+
+ // Package resources' paths should be cached in ResourcesManager.
+ assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
+ .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
+
+ // Revert the ResourcesManager instance back.
+ ResourcesManager.setInstance(oriResourcesManager);
+ }
+
+ private static boolean allResourcePathsLoaded(String[] resourcePaths, ApkAssets[] loadedAsset) {
+ for (int i = 0; i < resourcePaths.length; i++) {
+ if (!resourcePaths[i].endsWith(".apk")) {
+ continue;
+ }
+ boolean found = false;
+ for (int j = 0; j < loadedAsset.length; j++) {
+ if (loadedAsset[j].getAssetPath().equals(resourcePaths[i])) {
+ found = true;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static String[] removeDuplicates(String[] paths) {
+ var pathList = new ArrayList<String>();
+ var pathSet = new ArraySet<String>();
+ final int pathsLen = paths.length;
+ for (int i = 0; i < pathsLen; i++) {
+ if (pathSet.add(paths[i])) {
+ pathList.add(paths[i]);
+ }
+ }
+ return pathList.toArray(new String[0]);
+ }
}
diff --git a/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java
index da3a465ade7e..b61104d6f389 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java
@@ -27,7 +27,8 @@ import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java
index 613089c8777d..f058c160373c 100644
--- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java
@@ -27,7 +27,8 @@ import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Rule;
@@ -38,7 +39,6 @@ import org.mockito.junit.MockitoRule;
import java.util.function.Function;
-
@Presubmit
@SmallTest
public class FingerprintSensorConfigurationsTest {
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index cb281ff8a6a7..b9a12ad57c33 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -32,6 +32,7 @@ import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
@@ -86,7 +87,8 @@ public class PowerManagerTest {
// Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
@Rule
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood() ? null
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
: DeviceFlagsValueProvider.createCheckFlagsRule();
/**
diff --git a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
index c70da6e94385..fcdc5905ef88 100644
--- a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
+++ b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
@@ -22,6 +22,7 @@ import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.runner.AndroidJUnit4;
@@ -40,7 +41,8 @@ public class WorkDurationUnitTest {
// Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
@Rule
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood() ? null
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
: DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 8c93fbbc6b47..48ba52621f64 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -24,8 +24,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.AdditionalMatchers.and;
+import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -36,6 +38,7 @@ import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
+import android.view.inputmethod.ImeTracker;
import android.widget.TextView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -96,12 +99,12 @@ public class ImeInsetsSourceConsumerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// test if setVisibility can show IME
mImeConsumer.onWindowFocusGained(true);
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertTrue((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
// test if setVisibility can hide IME
- mController.hide(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.hide(WindowInsets.Type.ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertFalse((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
});
@@ -114,8 +117,9 @@ public class ImeInsetsSourceConsumerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
+ final var statsToken = ImeTracker.Token.empty();
mImeConsumer.onWindowFocusGained(true);
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
// set control and verify visibility is applied.
InsetsSourceControl control = new InsetsSourceControl(ID_IME,
@@ -124,10 +128,10 @@ public class ImeInsetsSourceConsumerTest {
// IME show animation should be triggered when control becomes available.
verify(mController).applyAnimation(
eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
- any() /* statsToken */);
+ eq(statsToken));
verify(mController, never()).applyAnimation(
eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */,
- any() /* statsToken */);
+ eq(statsToken));
});
}
@@ -152,9 +156,9 @@ public class ImeInsetsSourceConsumerTest {
// Request IME visible before control is available.
mImeConsumer.onWindowFocusGained(hasWindowFocus);
final boolean imeVisible = hasWindowFocus && hasViewFocus;
+ final var statsToken = ImeTracker.Token.empty();
if (imeVisible) {
- mController.show(WindowInsets.Type.ime(), true /* fromIme */,
- null /* statsToken */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
}
// set control and verify visibility is applied.
@@ -168,23 +172,25 @@ public class ImeInsetsSourceConsumerTest {
// and expect skip animation state after getAndClearSkipAnimationOnce invoked.
mController.onControlsChanged(new InsetsSourceControl[]{ control });
verify(control).getAndClearSkipAnimationOnce();
+ // This ends up creating a new request when we gain control,
+ // so the statsToken won't match.
verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
eq(true) /* show */, eq(false) /* fromIme */,
- eq(expectSkipAnim) /* skipAnim */, eq(null) /* statsToken */);
+ eq(expectSkipAnim) /* skipAnim */, and(not(eq(statsToken)), notNull()));
}
// If previously hasViewFocus is false, verify when requesting the IME visible next
// time will not skip animation.
if (!hasViewFocus) {
- mController.show(WindowInsets.Type.ime(), true /* fromIme */,
- null /* statsToken */);
+ final var statsTokenNext = ImeTracker.Token.empty();
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsTokenNext);
mController.onControlsChanged(new InsetsSourceControl[]{ control });
// Verify IME show animation should be triggered when control becomes available and
// the animation will be skipped by getAndClearSkipAnimationOnce invoked.
verify(control).getAndClearSkipAnimationOnce();
verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
eq(true) /* show */, eq(true) /* fromIme */,
- eq(false) /* skipAnim */, eq(null) /* statsToken */);
+ eq(false) /* skipAnim */, eq(statsTokenNext));
}
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 1568174e1955..316e191eecbd 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -256,7 +256,7 @@ public class InsetsControllerTest {
mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
// When using the animation thread, this must not invoke onReady()
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
});
@@ -273,14 +273,14 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.show(all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimations();
- final @InsetsType int types = navigationBars() | statusBars() | ime();
+ @InsetsType final int types = navigationBars() | statusBars() | ime();
assertEquals(types, mController.getRequestedVisibleTypes() & types);
- mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+ mController.hide(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.hide(all());
mController.cancelExistingAnimations();
assertEquals(0, mController.getRequestedVisibleTypes() & types);
@@ -295,10 +295,10 @@ public class InsetsControllerTest {
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertTrue(isRequestedVisible(mController, ime()));
- mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+ mController.hide(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertFalse(isRequestedVisible(mController, ime()));
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusLost();
@@ -465,7 +465,7 @@ public class InsetsControllerTest {
assertFalse(mController.getState().peekSource(ID_IME).isVisible());
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
// Gaining control shortly after
mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
@@ -489,7 +489,7 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
mController.cancelExistingAnimations();
@@ -567,7 +567,7 @@ public class InsetsControllerTest {
verify(listener, never()).onReady(any(), anyInt());
// Pretend that IME is calling.
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -651,7 +651,7 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
copy.peekSource(ID_IME).setFrame(0, 1, 2, 3);
@@ -851,7 +851,7 @@ public class InsetsControllerTest {
// Showing invisible ime should only causes insets change once.
clearInvocations(mTestHost);
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
verify(mTestHost, times(1)).notifyInsetsChanged();
// Sending the same insets state should not cause insets change.
@@ -918,7 +918,7 @@ public class InsetsControllerTest {
assertNull(imeInsetsConsumer.getControl());
// Verify IME requested visibility should be updated to IME consumer from controller.
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
assertTrue(isRequestedVisible(mController, ime()));
mController.hide(ime());
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 7c58de67ded6..1a242eff73b1 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -451,7 +451,7 @@ public class ViewRootImplTest {
ViewRootImpl viewRootImpl = new ViewRootImpl(sContext, display);
boolean result = viewRootImpl.performHapticFeedback(
- HapticFeedbackConstants.CONTEXT_CLICK, true);
+ HapticFeedbackConstants.CONTEXT_CLICK, true, false /* fromIme */);
assertThat(result).isFalse();
}
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 51eb41c5a271..b60b806f3444 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -472,6 +472,26 @@ public class HandwritingInitiatorTest {
}
@Test
+ public void onTouchEvent_doesNothing_viewDisabled() {
+ mTestView1.setEnabled(false);
+
+ final int x1 = (sHwArea1.left + sHwArea1.right) / 2;
+ final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + mHandwritingSlop * 2;
+ final int y2 = y1;
+
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ // HandwritingInitiator will not request focus if it is disabled.
+ verify(mTestView1, never()).requestFocus();
+ verify(mHandwritingInitiator, never()).startHandwriting(mTestView1);
+ }
+
+ @Test
public void onTouchEvent_focusView_inputConnectionAlreadyBuilt_stylusMoveOnce_withinHWArea() {
if (!mInitiateWithoutConnection) {
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
diff --git a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
index df212ebe1744..cd38bd68a26b 100644
--- a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
+++ b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
@@ -16,11 +16,17 @@
package android.widget;
+import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.AttributeSet;
import android.util.PollingCheck;
import androidx.test.filters.MediumTest;
@@ -43,14 +49,21 @@ import java.util.concurrent.TimeUnit;
public class HorizontalScrollViewFunctionalTest {
private HorizontalScrollViewActivity mActivity;
private HorizontalScrollView mHorizontalScrollView;
+ private MyHorizontalScrollView mMyHorizontalScrollView;
@Rule
public ActivityTestRule<HorizontalScrollViewActivity> mActivityRule = new ActivityTestRule<>(
HorizontalScrollViewActivity.class);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws Exception {
mActivity = mActivityRule.getActivity();
mHorizontalScrollView = mActivity.findViewById(R.id.horizontal_scroll_view);
+ mMyHorizontalScrollView =
+ (MyHorizontalScrollView) mActivity.findViewById(R.id.my_horizontal_scroll_view);
}
@Test
@@ -79,6 +92,22 @@ public class HorizontalScrollViewFunctionalTest {
assertEquals(maxScroll, mHorizontalScrollView.getScrollX());
}
+ @Test
+ @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
+ public void testSetVelocity() throws Throwable {
+ mActivityRule.runOnUiThread(() -> {
+ mMyHorizontalScrollView.setFrameContentVelocity(0);
+ });
+ // set setFrameContentVelocity shouldn't do anything.
+ assertEquals(mMyHorizontalScrollView.isSetVelocityCalled, false);
+
+ mActivityRule.runOnUiThread(() -> {
+ mMyHorizontalScrollView.fling(100);
+ });
+ // set setFrameContentVelocity should be called when fling.
+ assertEquals(mMyHorizontalScrollView.isSetVelocityCalled, true);
+ }
+
static class WatchedEdgeEffect extends EdgeEffect {
public CountDownLatch onAbsorbLatch = new CountDownLatch(1);
@@ -92,5 +121,29 @@ public class HorizontalScrollViewFunctionalTest {
onAbsorbLatch.countDown();
}
}
+
+ public static class MyHorizontalScrollView extends ScrollView {
+
+ public boolean isSetVelocityCalled;
+
+ public MyHorizontalScrollView(Context context) {
+ super(context);
+ }
+
+ public MyHorizontalScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void setFrameContentVelocity(float pixelsPerSecond) {
+ if (pixelsPerSecond != 0) {
+ isSetVelocityCalled = true;
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 4f722cefcf9f..6ab77dc9d535 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -355,7 +355,7 @@ public class RemoteViewsAdapterTest {
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
new RemoteViews.RemoteCollectionItems.Builder();
itemsBuilder.setHasStableIds(hasStableIds())
diff --git a/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java b/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java
index 109c8080de94..a60b2a13e2eb 100644
--- a/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java
+++ b/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java
@@ -16,11 +16,17 @@
package android.widget;
+import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.AttributeSet;
import android.util.PollingCheck;
import androidx.test.filters.MediumTest;
@@ -43,14 +49,20 @@ import java.util.concurrent.TimeUnit;
public class ScrollViewFunctionalTest {
private ScrollViewActivity mActivity;
private ScrollView mScrollView;
+ private MyScrollView mMyScrollView;
@Rule
public ActivityTestRule<ScrollViewActivity> mActivityRule = new ActivityTestRule<>(
ScrollViewActivity.class);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws Exception {
mActivity = mActivityRule.getActivity();
mScrollView = mActivity.findViewById(R.id.scroll_view);
+ mMyScrollView = (MyScrollView) mActivity.findViewById(R.id.my_scroll_view);
}
@Test
@@ -79,6 +91,22 @@ public class ScrollViewFunctionalTest {
assertEquals(maxScroll, mScrollView.getScrollY());
}
+ @Test
+ @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
+ public void testSetVelocity() throws Throwable {
+ mActivityRule.runOnUiThread(() -> {
+ mMyScrollView.setFrameContentVelocity(0);
+ });
+ // set setFrameContentVelocity shouldn't do anything.
+ assertEquals(mMyScrollView.isSetVelocityCalled, false);
+
+ mActivityRule.runOnUiThread(() -> {
+ mMyScrollView.fling(100);
+ });
+ // set setFrameContentVelocity should be called when fling.
+ assertEquals(mMyScrollView.isSetVelocityCalled, true);
+ }
+
static class WatchedEdgeEffect extends EdgeEffect {
public CountDownLatch onAbsorbLatch = new CountDownLatch(1);
@@ -92,5 +120,29 @@ public class ScrollViewFunctionalTest {
onAbsorbLatch.countDown();
}
}
+
+ public static class MyScrollView extends ScrollView {
+
+ public boolean isSetVelocityCalled;
+
+ public MyScrollView(Context context) {
+ super(context);
+ }
+
+ public MyScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void setFrameContentVelocity(float pixelsPerSecond) {
+ if (pixelsPerSecond != 0) {
+ isSetVelocityCalled = true;
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index 58cfc6625051..43e62275152e 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -53,6 +53,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -93,6 +94,9 @@ public class IntentForwarderActivityTest {
private static final String TYPE_PLAIN_TEXT = "text/plain";
private static UserInfo MANAGED_PROFILE_INFO = new UserInfo();
+ private static UserInfo PRIVATE_PROFILE_INFO = new UserInfo(12, "Private", null,
+ UserInfo.FLAG_PROFILE, UserManager.USER_TYPE_PROFILE_PRIVATE);
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
static {
MANAGED_PROFILE_INFO.id = 10;
@@ -131,6 +135,7 @@ public class IntentForwarderActivityTest {
@Before
public void setup() {
+
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getTargetContext();
sInjector = spy(new TestInjector());
@@ -632,6 +637,54 @@ public class IntentForwarderActivityTest {
logMakerCaptor.getValue().getSubtype());
}
+ @Test
+ public void shouldForwardToParent_telephony_privateProfile() throws Exception {
+ mSetFlagsRule.enableFlags(
+ android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_INTENT_REDIRECTION);
+
+ sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME;
+ when(mIPm.canForwardTo(
+ any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
+
+ List<UserInfo> profiles = new ArrayList<>();
+ profiles.add(CURRENT_USER_INFO);
+ profiles.add(PRIVATE_PROFILE_INFO);
+ when(mUserManager.getProfiles(anyInt())).thenReturn(profiles);
+ when(mUserManager.getProfileParent(anyInt())).thenReturn(CURRENT_USER_INFO);
+ Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class);
+ intent.setAction(Intent.ACTION_DIAL);
+ IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent);
+ verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+ assertEquals(activity.getStartActivityIntent().getAction(), intent.getAction());
+ assertEquals(activity.getUserIdActivityLaunchedIn(), CURRENT_USER_INFO.id);
+ }
+
+ @Test
+ public void shouldForwardToParent_mms_privateProfile() throws Exception {
+ mSetFlagsRule.enableFlags(
+ android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_INTENT_REDIRECTION);
+
+ sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME;
+ when(mIPm.canForwardTo(
+ any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
+
+ List<UserInfo> profiles = new ArrayList<>();
+ profiles.add(CURRENT_USER_INFO);
+ profiles.add(PRIVATE_PROFILE_INFO);
+ when(mUserManager.getProfiles(anyInt())).thenReturn(profiles);
+ when(mUserManager.getProfileParent(anyInt())).thenReturn(CURRENT_USER_INFO);
+ Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class);
+ intent.setAction(Intent.ACTION_SEND);
+ intent.setType(TYPE_PLAIN_TEXT);
+ IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent);
+ verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+ assertEquals(activity.getStartActivityIntent().getAction(), intent.getAction());
+ assertEquals(activity.getStartActivityIntent().getType(), intent.getType());
+ assertEquals(activity.getUserIdActivityLaunchedIn(), CURRENT_USER_INFO.id);
+ }
+
private void setupShouldSkipDisclosureTest() throws RemoteException {
sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME;
sActivityName = "MyTestActivity";
@@ -688,6 +741,14 @@ public class IntentForwarderActivityTest {
protected MetricsLogger getMetricsLogger() {
return mMetricsLogger;
}
+
+ Intent getStartActivityIntent() {
+ return mStartActivityIntent;
+ }
+
+ int getUserIdActivityLaunchedIn() {
+ return mUserIdActivityLaunchedIn;
+ }
}
public class TestInjector implements IntentForwarderActivity.Injector {
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index 316042874201..cb8b0db07249 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -24,9 +24,9 @@ import static junit.framework.Assert.assertTrue;
import android.content.Context;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 66be05ff233c..ed641e048a81 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -309,17 +309,17 @@ public class ActivityThreadClientTest {
private void pauseActivity(ActivityClientRecord r) {
mThread.handlePauseActivity(r, false /* finished */,
- false /* userLeaving */, 0 /* configChanges */, false /* autoEnteringPip */,
+ false /* userLeaving */, false /* autoEnteringPip */,
null /* pendingActions */, "test");
}
private void stopActivity(ActivityClientRecord r) {
- mThread.handleStopActivity(r, 0 /* configChanges */,
+ mThread.handleStopActivity(r,
new PendingTransactionActions(), false /* finalStateRequest */, "test");
}
private void destroyActivity(ActivityClientRecord r) {
- mThread.handleDestroyActivity(r, true /* finishing */, 0 /* configChanges */,
+ mThread.handleDestroyActivity(r, true /* finishing */,
false /* getNonConfigInstance */, "test");
}
diff --git a/core/tests/systemproperties/Android.bp b/core/tests/systemproperties/Android.bp
index 21aa3c44044b..ed52cccfb9b9 100644
--- a/core/tests/systemproperties/Android.bp
+++ b/core/tests/systemproperties/Android.bp
@@ -14,9 +14,9 @@ android_test {
dxflags: ["--core-library"],
static_libs: [
"android-common",
- "frameworks-core-util-lib",
"androidx.test.rules",
"androidx.test.ext.junit",
+ "frameworks-core-util-lib",
"ravenwood-junit",
],
libs: [
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index ea65de088c07..d98120f535d8 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -24,7 +24,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.platform.test.ravenwood.RavenwoodRule;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 238a3e10f058..1410950966e9 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -72,6 +72,12 @@ prebuilt_etc {
src: "enhanced-confirmation.xml",
}
+prebuilt_etc {
+ name: "package-shareduid-allowlist.xml",
+ sub_dir: "sysconfig",
+ src: "package-shareduid-allowlist.xml",
+}
+
// Privapp permission whitelist files
prebuilt_etc {
diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk
index 783a7edadeb7..fd38d2782cb2 100644
--- a/data/etc/CleanSpec.mk
+++ b/data/etc/CleanSpec.mk
@@ -43,6 +43,8 @@
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/sysconfig/package-shareduid-allowlist.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/sysconfig/package-shareduid-allowlist.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.carrierconfig.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.carrierconfig.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.emergency.xml)
diff --git a/data/etc/package-shareduid-allowlist.xml b/data/etc/package-shareduid-allowlist.xml
new file mode 100644
index 000000000000..2401d4a26e68
--- /dev/null
+++ b/data/etc/package-shareduid-allowlist.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<!--
+This XML defines an allowlist for packages that want to join a particular shared-uid.
+If a non-system package that is signed with platform signature, is trying to join a particular
+shared-uid, and not in this list, the installation will fail.
+
+- The "package" XML attribute refers to the app's package name.
+- The "shareduid" XML attribute refers to the shared uid name.
+
+Example usage
+ 1. <allow-package-shareduid package="com.example.app" shareduid="android.uid.system"/>
+ Indicates that a package - com.example.app, will be able to join android.uid.system.
+ 2. <allow-package-shareduid package="oem.example.app" shareduid="oem.uid.custom"/>
+ Indicates that a package - oem.example.app, will be able to join oem.uid.custom.
+-->
+
+<config>
+ <allow-package-shareduid package="android.test.settings" shareduid="android.uid.system" />
+</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4edfb0943ad5..051e73f4d4c8 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -584,6 +584,8 @@ applications that come with the platform
<permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
<!-- Permission required for CTS test CtsInputTestCases -->
<permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" />
+ <!-- Permission required for CTS test - PackageManagerShellCommandInstallTest -->
+ <permission name="android.permission.EMERGENCY_INSTALL_PACKAGES" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index e8c7a53ddcff..0231d3abd19e 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1,5 +1,5 @@
{
- "version": "1.0.0",
+ "version": "2.0.0",
"messages": {
"7286191062634870297": {
"message": "Binding proc %s with config %s",
diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml
index 15ea15a9b4c1..53024ab858c4 100644
--- a/data/fonts/font_fallback.xml
+++ b/data/fonts/font_fallback.xml
@@ -792,14 +792,10 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
- <axis tag="wght" stylevalue="700"/>
- </font>
</family>
<family lang="ko">
<font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular">
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
index c1ca67ef456a..ac1b06495832 100644
--- a/data/fonts/font_fallback_cjkvf.xml
+++ b/data/fonts/font_fallback_cjkvf.xml
@@ -804,14 +804,10 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
- <axis tag="wght" stylevalue="700"/>
- </font>
</family>
<family lang="ko">
<font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index b23f00554bdb..d1aa8e9734c2 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -1433,12 +1433,12 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml
index 1ab71ae270eb..9545ae718574 100644
--- a/data/fonts/fonts_cjkvf.xml
+++ b/data/fonts/fonts_cjkvf.xml
@@ -1532,12 +1532,12 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
diff --git a/graphics/java/android/graphics/Matrix44.java b/graphics/java/android/graphics/Matrix44.java
index 7cc0eb7a6728..a99e20101c3b 100644
--- a/graphics/java/android/graphics/Matrix44.java
+++ b/graphics/java/android/graphics/Matrix44.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import com.android.graphics.hwui.flags.Flags;
@@ -98,11 +99,11 @@ public class Matrix44 {
/**
* Gets the value at the matrix's row and column.
*
- * @param row An integer from 0 to 4 indicating the row of the value to get
- * @param col An integer from 0 to 4 indicating the column of the value to get
+ * @param row An integer from 0 to 3 indicating the row of the value to get
+ * @param col An integer from 0 to 3 indicating the column of the value to get
*/
@FlaggedApi(Flags.FLAG_MATRIX_44)
- public float get(int row, int col) {
+ public float get(@IntRange(from = 0, to = 3) int row, @IntRange(from = 0, to = 3) int col) {
if (row >= 0 && row < 4 && col >= 0 && col < 4) {
return mBackingArray[row * 4 + col];
}
@@ -112,12 +113,13 @@ public class Matrix44 {
/**
* Sets the value at the matrix's row and column to the provided value.
*
- * @param row An integer from 0 to 4 indicating the row of the value to change
- * @param col An integer from 0 to 4 indicating the column of the value to change
+ * @param row An integer from 0 to 3 indicating the row of the value to change
+ * @param col An integer from 0 to 3 indicating the column of the value to change
* @param val The value the element at the specified index will be set to
*/
@FlaggedApi(Flags.FLAG_MATRIX_44)
- public void set(int row, int col, float val) {
+ public void set(@IntRange(from = 0, to = 3) int row, @IntRange(from = 0, to = 3) int col,
+ float val) {
if (row >= 0 && row < 4 && col >= 0 && col < 4) {
mBackingArray[row * 4 + col] = val;
} else {
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 6da07198c3ad..884268a4b85c 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -29,11 +29,13 @@ import android.util.Log;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.NeverInline;
import libcore.util.NativeAllocationRegistry;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -85,6 +87,30 @@ public class MeasuredText {
return mChars;
}
+ private void rangeCheck(int start, int end) {
+ if (start < 0 || start > end || end > mChars.length) {
+ throwRangeError(start, end);
+ }
+ }
+
+ @NeverInline
+ private void throwRangeError(int start, int end) {
+ throw new IllegalArgumentException(String.format(Locale.US,
+ "start(%d) end(%d) length(%d) out of bounds", start, end, mChars.length));
+ }
+
+ private void offsetCheck(int offset) {
+ if (offset < 0 || offset >= mChars.length) {
+ throwOffsetError(offset);
+ }
+ }
+
+ @NeverInline
+ private void throwOffsetError(int offset) {
+ throw new IllegalArgumentException(String.format(Locale.US,
+ "offset (%d) length(%d) out of bounds", offset, mChars.length));
+ }
+
/**
* Returns the width of a given range.
*
@@ -93,12 +119,7 @@ public class MeasuredText {
*/
public @FloatRange(from = 0.0) @Px float getWidth(
@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
- Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(%d) must be 0 <= start <= %d", start, mChars.length);
- Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(%d) must be 0 <= end <= %d", end, mChars.length);
- Preconditions.checkArgument(start <= end,
- "start(%d) is larger than end(%d)", start, end);
+ rangeCheck(start, end);
return nGetWidth(mNativePtr, start, end);
}
@@ -120,12 +141,7 @@ public class MeasuredText {
*/
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect rect) {
- Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(%d) must be 0 <= start <= %d", start, mChars.length);
- Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(%d) must be 0 <= end <= %d", end, mChars.length);
- Preconditions.checkArgument(start <= end,
- "start(%d) is larger than end(%d)", start, end);
+ rangeCheck(start, end);
Preconditions.checkNotNull(rect);
nGetBounds(mNativePtr, mChars, start, end, rect);
}
@@ -139,12 +155,7 @@ public class MeasuredText {
*/
public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Paint.FontMetricsInt outMetrics) {
- Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(%d) must be 0 <= start <= %d", start, mChars.length);
- Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(%d) must be 0 <= end <= %d", end, mChars.length);
- Preconditions.checkArgument(start <= end,
- "start(%d) is larger than end(%d)", start, end);
+ rangeCheck(start, end);
Objects.requireNonNull(outMetrics);
long packed = nGetExtent(mNativePtr, mChars, start, end);
@@ -160,8 +171,7 @@ public class MeasuredText {
* @param offset an offset of the character.
*/
public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) {
- Preconditions.checkArgument(0 <= offset && offset < mChars.length,
- "offset(%d) is larger than text length %d" + offset, mChars.length);
+ offsetCheck(offset);
return nGetCharWidthAt(mNativePtr, offset);
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 62fe54f1f089..ef03d3a3b286 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -19,9 +19,9 @@ package android.security.keystore;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.security.KeyStore;
import java.io.IOException;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
@@ -47,13 +47,13 @@ public class AndroidKeyStoreProvider extends Provider {
}
/**
- * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+ * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
* primitive.
*
* <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
*
- * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
- * is not in progress.
+ * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+ * KeyStore operation is not in progress.
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
@@ -67,10 +67,10 @@ public class AndroidKeyStoreProvider extends Provider {
}
/**
- * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
- * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
- * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
- * all of which are system.
+ * Returns an {@code AndroidKeyStore} {@link KeyStore} of the specified UID. The {@code
+ * KeyStore} contains keys and certificates owned by that UID. Such cross-UID access is
+ * permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN) all of which
+ * are system.
*
* <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
* no need to invoke {@code load} on it.
@@ -84,12 +84,12 @@ public class AndroidKeyStoreProvider extends Provider {
*/
@SystemApi
@NonNull
- public static java.security.KeyStore getKeyStoreForUid(int uid)
+ public static KeyStore getKeyStoreForUid(int uid)
throws KeyStoreException, NoSuchProviderException {
- final java.security.KeyStore.LoadStoreParameter loadParameter =
+ final KeyStore.LoadStoreParameter loadParameter =
new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
KeyProperties.legacyUidToNamespace(uid));
- java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
+ KeyStore result = KeyStore.getInstance(PROVIDER_NAME);
try {
result.load(loadParameter);
} catch (NoSuchAlgorithmException | CertificateException | IOException e) {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 244fe3033dca..7aecfd8d4a0d 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -910,7 +910,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
/**
* Returns whether this key is critical to the device encryption flow.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @see Builder#setCriticalToDeviceEncryption(boolean)
* @hide
*/
public boolean isCriticalToDeviceEncryption() {
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 2495d1a85864..31b4a5eac619 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -569,7 +569,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
/**
* Return whether this key is critical to the device encryption flow.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @see Builder#setCriticalToDeviceEncryption(boolean)
* @hide
*/
public boolean isCriticalToDeviceEncryption() {
@@ -1105,9 +1105,10 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
* Set whether this key is critical to the device encryption flow
*
* This is a special flag only available to system servers to indicate the current key
- * is part of the device encryption flow.
+ * is part of the device encryption flow. Setting this flag causes the key to not
+ * be cryptographically bound to the LSKF even if the key is otherwise authentication
+ * bound.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
*/
public Builder setCriticalToDeviceEncryption(boolean critical) {
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
index 2c709ae1ac5b..c42c9e4d99a6 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
@@ -16,18 +16,16 @@
package android.security.keystore;
-import android.security.KeyStore;
-
/**
- * Cryptographic operation backed by {@link KeyStore}.
+ * Cryptographic operation backed by Android KeyStore.
*
* @hide
*/
public interface KeyStoreCryptoOperation {
/**
- * Gets the KeyStore operation handle of this crypto operation.
+ * Gets the Android KeyStore operation handle of this crypto operation.
*
- * @return handle or {@code 0} if the KeyStore operation is not in progress.
+ * @return handle or {@code 0} if the Android KeyStore operation is not in progress.
*/
long getOperationHandle();
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
index a8dd7f3f8b14..8eca67f090d4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
@@ -16,7 +16,6 @@
package android.security.keystore2;
-import android.security.KeyStore;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyInfo;
@@ -39,8 +38,6 @@ import java.security.spec.X509EncodedKeySpec;
*/
public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
@Override
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d204f13d4d78..99100de12684 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -17,7 +17,6 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterDefs;
@@ -161,13 +160,13 @@ public class AndroidKeyStoreProvider extends Provider {
}
/**
- * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+ * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
* primitive.
*
* <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
*
- * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
- * is not in progress.
+ * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+ * KeyStore operation is not in progress.
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 2682eb657963..22230916b084 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -18,7 +18,6 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.security.GateKeeper;
-import android.security.KeyStore;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyGenParameterSpec;
@@ -46,8 +45,6 @@ import javax.crypto.spec.SecretKeySpec;
*/
public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
@Override
protected KeySpec engineGetKeySpec(SecretKey key,
@SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
index 07d6a69eda01..5bd98bce9f39 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
@@ -16,12 +16,11 @@
package android.security.keystore2;
-import android.security.KeyStore;
import android.security.KeyStoreException;
/**
- * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
- * {@code update} and {@code finish} operations.
+ * Helper for streaming a crypto operation's input and output via KeyStore service's {@code update}
+ * and {@code finish} operations.
*
* <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
* update and finish operations. Firstly, KeyStore's update operation can consume only a limited
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 08b7bb89d10c..39cfacec8447 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -201,7 +201,7 @@ class SplitContainer {
return null;
}
return new SplitInfo(primaryActivityStack, secondaryActivityStack,
- mCurrentSplitAttributes, mToken);
+ mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken));
}
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index ae3a854baf9f..1abda4287800 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -35,6 +35,7 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHA
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_ACTIVITY_STACK_TOKEN;
import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -55,6 +56,7 @@ import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Instrumentation;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -102,6 +104,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
/**
* Main controller class that manages split states and presentation.
@@ -112,10 +115,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
- // TODO(b/295993745): remove after prebuilt library is updated.
- private static final String KEY_ACTIVITY_STACK_TOKEN =
- "androidx.window.extensions.embedding.ActivityStackToken";
-
@VisibleForTesting
@GuardedBy("mLock")
final SplitPresenter mPresenter;
@@ -181,6 +180,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private final List<ActivityStack> mLastReportedActivityStacks = new ArrayList<>();
+ /** WM Jetpack set callback for {@link EmbeddedActivityWindowInfo}. */
+ @GuardedBy("mLock")
+ @Nullable
+ private Pair<Executor, Consumer<EmbeddedActivityWindowInfo>>
+ mEmbeddedActivityWindowInfoCallback;
+
+ /** Listener registered to {@link ClientTransactionListenerController}. */
+ @GuardedBy("mLock")
+ @Nullable
+ private final BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener =
+ Flags.activityWindowInfoFlag()
+ ? this::onActivityWindowInfoChanged
+ : null;
+
private final Handler mHandler;
final Object mLock = new Object();
private final ActivityStartMonitor mActivityStartMonitor;
@@ -554,7 +567,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
- public void updateActivityStackAttributes(@NonNull IBinder activityStackToken,
+ public void updateActivityStackAttributes(@NonNull ActivityStack.Token activityStackToken,
@NonNull ActivityStackAttributes attributes) {
if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
return;
@@ -563,7 +576,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
Objects.requireNonNull(attributes);
synchronized (mLock) {
- final TaskFragmentContainer container = getContainer(activityStackToken);
+ final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken());
if (container == null) {
Log.w(TAG, "Cannot find TaskFragmentContainer for token:" + activityStackToken);
return;
@@ -583,13 +596,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
@Nullable
- public ParentContainerInfo getParentContainerInfo(@NonNull IBinder activityStackToken) {
+ public ParentContainerInfo getParentContainerInfo(
+ @NonNull ActivityStack.Token activityStackToken) {
if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
return null;
}
Objects.requireNonNull(activityStackToken);
synchronized (mLock) {
- final TaskFragmentContainer container = getContainer(activityStackToken);
+ final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken());
if (container == null) {
return null;
}
@@ -601,7 +615,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
@Nullable
- public IBinder getActivityStackToken(@NonNull String tag) {
+ public ActivityStack.Token getActivityStackToken(@NonNull String tag) {
if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
return null;
}
@@ -612,7 +626,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (taskFragmentContainer == null) {
return null;
}
- return taskFragmentContainer.getTaskFragmentToken();
+ return ActivityStack.Token.createFromBinder(taskFragmentContainer
+ .getTaskFragmentToken());
}
}
@@ -2457,6 +2472,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@VisibleForTesting
+ @Nullable
+ ActivityThread.ActivityClientRecord getActivityClientRecord(@NonNull Activity activity) {
+ return ActivityThread.currentActivityThread()
+ .getActivityClient(activity.getActivityToken());
+ }
+
+ @VisibleForTesting
ActivityStartMonitor getActivityStartMonitor() {
return mActivityStartMonitor;
}
@@ -2469,8 +2491,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@VisibleForTesting
@Nullable
IBinder getTaskFragmentTokenFromActivityClientRecord(@NonNull Activity activity) {
- final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread()
- .getActivityClient(activity.getActivityToken());
+ final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity);
return record != null ? record.mTaskFragmentToken : null;
}
@@ -2761,8 +2782,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// TODO(b/232042367): Consolidate the activity create handling so that we can handle
// cross-process the same as normal.
- IBinder activityStackToken = options.getBinder(KEY_ACTIVITY_STACK_TOKEN);
- if (activityStackToken != null) {
+ final Bundle bundle = options.getBundle(KEY_ACTIVITY_STACK_TOKEN);
+ if (bundle != null) {
+ final IBinder activityStackToken = ActivityStack.Token.readFromBundle(bundle)
+ .getRawToken();
// Put activityStack token to #KEY_LAUNCH_TASK_FRAGMENT_TOKEN to launch the activity
// into the taskFragment associated with the token.
options.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN, activityStackToken);
@@ -2875,17 +2898,102 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
+ @Override
+ public void setEmbeddedActivityWindowInfoCallback(@NonNull Executor executor,
+ @NonNull Consumer<EmbeddedActivityWindowInfo> callback) {
+ if (!Flags.activityWindowInfoFlag()) {
+ return;
+ }
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ synchronized (mLock) {
+ if (mEmbeddedActivityWindowInfoCallback == null) {
+ ClientTransactionListenerController.getInstance()
+ .registerActivityWindowInfoChangedListener(getActivityWindowInfoListener());
+ }
+ mEmbeddedActivityWindowInfoCallback = new Pair<>(executor, callback);
+ }
+ }
+
+ @Override
+ public void clearEmbeddedActivityWindowInfoCallback() {
+ if (!Flags.activityWindowInfoFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (mEmbeddedActivityWindowInfoCallback == null) {
+ return;
+ }
+ mEmbeddedActivityWindowInfoCallback = null;
+ ClientTransactionListenerController.getInstance()
+ .unregisterActivityWindowInfoChangedListener(getActivityWindowInfoListener());
+ }
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ @Nullable
+ BiConsumer<IBinder, ActivityWindowInfo> getActivityWindowInfoListener() {
+ return mActivityWindowInfoListener;
+ }
+
+ @Nullable
+ @Override
+ public EmbeddedActivityWindowInfo getEmbeddedActivityWindowInfo(@NonNull Activity activity) {
+ if (!Flags.activityWindowInfoFlag()) {
+ return null;
+ }
+ synchronized (mLock) {
+ final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity);
+ return activityWindowInfo != null
+ ? translateActivityWindowInfo(activity, activityWindowInfo)
+ : null;
+ }
+ }
+
+ @VisibleForTesting
+ void onActivityWindowInfoChanged(@NonNull IBinder activityToken,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
+ synchronized (mLock) {
+ if (mEmbeddedActivityWindowInfoCallback == null) {
+ return;
+ }
+ final Executor executor = mEmbeddedActivityWindowInfoCallback.first;
+ final Consumer<EmbeddedActivityWindowInfo> callback =
+ mEmbeddedActivityWindowInfoCallback.second;
+
+ final Activity activity = getActivity(activityToken);
+ if (activity == null) {
+ return;
+ }
+ final EmbeddedActivityWindowInfo info = translateActivityWindowInfo(
+ activity, activityWindowInfo);
+
+ executor.execute(() -> callback.accept(info));
+ }
+ }
+
@Nullable
- private static ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) {
+ private ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) {
if (activity.isFinishing()) {
return null;
}
- final ActivityThread.ActivityClientRecord record =
- ActivityThread.currentActivityThread()
- .getActivityClient(activity.getActivityToken());
+ final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity);
return record != null ? record.getActivityWindowInfo() : null;
}
+ @NonNull
+ private static EmbeddedActivityWindowInfo translateActivityWindowInfo(
+ @NonNull Activity activity, @NonNull ActivityWindowInfo activityWindowInfo) {
+ final boolean isEmbedded = activityWindowInfo.isEmbedded();
+ final Rect activityBounds = new Rect(activity.getResources().getConfiguration()
+ .windowConfiguration.getBounds());
+ final Rect taskBounds = new Rect(activityWindowInfo.getTaskBounds());
+ final Rect activityStackBounds = new Rect(activityWindowInfo.getTaskFragmentBounds());
+ return new EmbeddedActivityWindowInfo(activity, isEmbedded, activityBounds, taskBounds,
+ activityStackBounds);
+ }
+
/**
* If the two rules have the same presentation, and the calculated {@link SplitAttributes}
* matches the {@link SplitAttributes} of {@link SplitContainer}, we can reuse the same
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 2f2da8c53db0..b53b9c519cb6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -387,7 +387,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// Sets the dim area when the two TaskFragments are adjacent.
final boolean dimOnTask = !isStacked
- && splitAttributes.getWindowAttributes().getDimArea() == DIM_AREA_ON_TASK
+ && splitAttributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
&& Flags.fullscreenDimFlag();
setTaskFragmentDimOnTask(wct, primaryContainer.getTaskFragmentToken(), dimOnTask);
setTaskFragmentDimOnTask(wct, secondaryContainer.getTaskFragmentToken(), dimOnTask);
@@ -590,7 +590,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final boolean isFillParent = relativeBounds.isEmpty();
final boolean isIsolatedNavigated = !isFillParent && container.isOverlay();
final boolean dimOnTask = !isFillParent
- && attributes.getWindowAttributes().getDimArea() == DIM_AREA_ON_TASK
+ && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
&& Flags.fullscreenDimFlag();
final IBinder fragmentToken = container.getTaskFragmentToken();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 6fe8e50f105f..a6bf99d4add5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -367,7 +367,8 @@ class TaskFragmentContainer {
if (activities == null) {
return null;
}
- return new ActivityStack(activities, isEmpty(), mToken, mOverlayTag);
+ return new ActivityStack(activities, isEmpty(),
+ ActivityStack.Token.createFromBinder(mToken), mOverlayTag);
}
/** Adds the activity that will be reparented to this container. */
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 34d43ad56bb4..28fbadbebe7f 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -399,7 +399,8 @@ public class OverlayPresentationTest {
new ActivityStackAttributes.Builder().build()));
assertThrows(NullPointerException.class, () ->
- mSplitController.updateActivityStackAttributes(new Binder(), null));
+ mSplitController.updateActivityStackAttributes(
+ ActivityStack.Token.createFromBinder(new Binder()), null));
verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
}
@@ -408,7 +409,8 @@ public class OverlayPresentationTest {
public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
mActivity.getTaskId());
- mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ mSplitController.updateActivityStackAttributes(
+ ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
new ActivityStackAttributes.Builder().build());
verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
@@ -418,7 +420,8 @@ public class OverlayPresentationTest {
public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() {
final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
- mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ mSplitController.updateActivityStackAttributes(
+ ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
new ActivityStackAttributes.Builder().build());
verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
@@ -431,7 +434,8 @@ public class OverlayPresentationTest {
final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build();
final IBinder token = container.getTaskFragmentToken();
- mSplitController.updateActivityStackAttributes(token, attrs);
+ mSplitController.updateActivityStackAttributes(ActivityStack.Token.createFromBinder(token),
+ attrs);
verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs),
any());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index b60943a60076..bdeeb7304b12 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -72,6 +72,8 @@ import static org.mockito.Mockito.times;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.ActivityThread;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -83,9 +85,11 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import android.view.WindowInsets;
import android.view.WindowMetrics;
+import android.window.ActivityWindowInfo;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentParentInfo;
@@ -99,7 +103,10 @@ import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -110,6 +117,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -127,6 +136,9 @@ public class SplitControllerTest {
private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
new ComponentName("test", "placeholder"));
+ @Rule
+ public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
+
private Activity mActivity;
@Mock
private Resources mActivityResources;
@@ -138,6 +150,13 @@ public class SplitControllerTest {
private Handler mHandler;
@Mock
private WindowLayoutComponentImpl mWindowLayoutComponent;
+ @Mock
+ private ActivityWindowInfo mActivityWindowInfo;
+ @Mock
+ private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
+ @Mock
+ private androidx.window.extensions.core.util.function.Consumer<EmbeddedActivityWindowInfo>
+ mEmbeddedActivityWindowInfoCallback;
private SplitController mSplitController;
private SplitPresenter mSplitPresenter;
@@ -1437,7 +1456,7 @@ public class SplitControllerTest {
@Test
public void testUpdateSplitAttributes_nullParams_throwException() {
assertThrows(NullPointerException.class,
- () -> mSplitController.updateSplitAttributes(null, SPLIT_ATTRIBUTES));
+ () -> mSplitController.updateSplitAttributes((IBinder) null, SPLIT_ATTRIBUTES));
final SplitContainer splitContainer = mock(SplitContainer.class);
final IBinder token = new Binder();
@@ -1529,6 +1548,73 @@ public class SplitControllerTest {
.getTopNonFinishingActivity(), secondaryActivity);
}
+ @Test
+ public void testIsActivityEmbedded() {
+ mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ assertFalse(mSplitController.isActivityEmbedded(mActivity));
+
+ doReturn(true).when(mActivityWindowInfo).isEmbedded();
+
+ assertTrue(mSplitController.isActivityEmbedded(mActivity));
+ }
+
+ @Test
+ public void testGetEmbeddedActivityWindowInfo() {
+ mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ final boolean isEmbedded = true;
+ final Rect activityBounds = mActivity.getResources().getConfiguration().windowConfiguration
+ .getBounds();
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ final Rect activityStackBounds = new Rect(0, 0, 500, 2000);
+ doReturn(isEmbedded).when(mActivityWindowInfo).isEmbedded();
+ doReturn(taskBounds).when(mActivityWindowInfo).getTaskBounds();
+ doReturn(activityStackBounds).when(mActivityWindowInfo).getTaskFragmentBounds();
+
+ final EmbeddedActivityWindowInfo expected = new EmbeddedActivityWindowInfo(mActivity,
+ isEmbedded, activityBounds, taskBounds, activityStackBounds);
+ assertEquals(expected, mSplitController.getEmbeddedActivityWindowInfo(mActivity));
+ }
+
+ @Test
+ public void testSetEmbeddedActivityWindowInfoCallback() {
+ mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ final ClientTransactionListenerController controller = ClientTransactionListenerController
+ .getInstance();
+ spyOn(controller);
+ doNothing().when(controller).registerActivityWindowInfoChangedListener(any());
+ doReturn(mActivityWindowInfoListener).when(mSplitController)
+ .getActivityWindowInfoListener();
+ final Executor executor = Runnable::run;
+
+ // Register to ClientTransactionListenerController
+ mSplitController.setEmbeddedActivityWindowInfoCallback(executor,
+ mEmbeddedActivityWindowInfoCallback);
+
+ verify(controller).registerActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+ verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any());
+
+ // Test onActivityWindowInfoChanged triggered.
+ mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(),
+ mActivityWindowInfo);
+
+ verify(mEmbeddedActivityWindowInfoCallback).accept(any());
+
+ // Unregister to ClientTransactionListenerController
+ mSplitController.clearEmbeddedActivityWindowInfoCallback();
+
+ verify(controller).unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+
+ // Test onActivityWindowInfoChanged triggered as no-op after clear callback.
+ clearInvocations(mEmbeddedActivityWindowInfoCallback);
+ mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(),
+ mActivityWindowInfo);
+
+ verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any());
+ }
+
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity() {
return createMockActivity(TASK_ID);
@@ -1537,13 +1623,17 @@ public class SplitControllerTest {
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity(int taskId) {
final Activity activity = mock(Activity.class);
+ final ActivityThread.ActivityClientRecord activityClientRecord =
+ mock(ActivityThread.ActivityClientRecord.class);
doReturn(mActivityResources).when(activity).getResources();
final IBinder activityToken = new Binder();
doReturn(activityToken).when(activity).getActivityToken();
doReturn(activity).when(mSplitController).getActivity(activityToken);
+ doReturn(activityClientRecord).when(mSplitController).getActivityClientRecord(activity);
doReturn(taskId).when(activity).getTaskId();
doReturn(new ActivityInfo()).when(activity).getActivityInfo();
doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
+ doReturn(mActivityWindowInfo).when(activityClientRecord).getActivityWindowInfo();
return activity;
}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 0ecf1f8f1feb..8829d1b9e0e1 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -212,76 +212,3 @@ android_library {
plugins: ["dagger2-compiler"],
use_resource_processor: true,
}
-
-android_app {
- name: "WindowManagerShellRobolectric",
- platform_apis: true,
- static_libs: [
- "WindowManager-Shell",
- ],
- manifest: "multivalentTests/AndroidManifestRobolectric.xml",
- use_resource_processor: true,
-}
-
-android_robolectric_test {
- name: "WMShellRobolectricTests",
- instrumentation_for: "WindowManagerShellRobolectric",
- upstream: true,
- java_resource_dirs: [
- "multivalentTests/robolectric/config",
- ],
- srcs: [
- "multivalentTests/src/**/*.kt",
- ],
- // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
- exclude_srcs: ["multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
- static_libs: [
- "junit",
- "androidx.test.runner",
- "androidx.test.rules",
- "androidx.test.ext.junit",
- "mockito-robolectric-prebuilt",
- "mockito-kotlin2",
- "truth",
- ],
-}
-
-android_test {
- name: "WMShellMultivalentTestsOnDevice",
- srcs: [
- "multivalentTests/src/**/*.kt",
- ],
- static_libs: [
- "WindowManager-Shell",
- "junit",
- "androidx.test.runner",
- "androidx.test.rules",
- "androidx.test.ext.junit",
- "frameworks-base-testutils",
- "mockito-kotlin2",
- "mockito-target-extended-minus-junit4",
- "truth",
- "platform-test-annotations",
- "platform-test-rules",
- ],
- libs: [
- "android.test.base",
- "android.test.runner",
- ],
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
- kotlincflags: ["-Xjvm-default=all"],
- optimize: {
- enabled: false,
- },
- test_suites: ["device-tests"],
- platform_apis: true,
- certificate: "platform",
- aaptflags: [
- "--extra-packages",
- "com.android.wm.shell",
- ],
- manifest: "multivalentTests/AndroidManifest.xml",
-}
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
new file mode 100644
index 000000000000..1686d0d54dc4
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -0,0 +1,97 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_multitasking_windowing",
+}
+
+android_app {
+ name: "WindowManagerShellRobolectric",
+ platform_apis: true,
+ static_libs: [
+ "WindowManager-Shell",
+ ],
+ manifest: "AndroidManifestRobolectric.xml",
+ use_resource_processor: true,
+}
+
+android_robolectric_test {
+ name: "WMShellRobolectricTests",
+ instrumentation_for: "WindowManagerShellRobolectric",
+ upstream: true,
+ java_resource_dirs: [
+ "robolectric/config",
+ ],
+ srcs: [
+ "src/**/*.kt",
+ ],
+ // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
+ exclude_srcs: ["src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
+ static_libs: [
+ "junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "mockito-robolectric-prebuilt",
+ "mockito-kotlin2",
+ "truth",
+ ],
+ auto_gen_config: true,
+}
+
+android_test {
+ name: "WMShellMultivalentTestsOnDevice",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "WindowManager-Shell",
+ "junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "frameworks-base-testutils",
+ "mockito-kotlin2",
+ "mockito-target-extended-minus-junit4",
+ "truth",
+ "platform-test-annotations",
+ "platform-test-rules",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: [
+ "--extra-packages",
+ "com.android.wm.shell",
+ ],
+ manifest: "AndroidManifest.xml",
+}
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml
new file mode 100644
index 000000000000..ff49edb7a699
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 490f0883fbfb..a5605a7ff50a 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -33,14 +33,15 @@
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
- android:paddingStart="6dp"
- android:paddingEnd="8dp">
+ android:paddingStart="12dp">
<ImageView
android:id="@+id/application_icon"
android:layout_width="@dimen/desktop_mode_caption_icon_radius"
android:layout_height="@dimen/desktop_mode_caption_icon_radius"
android:layout_gravity="center_vertical"
- android:contentDescription="@string/app_icon_text" />
+ android:contentDescription="@string/app_icon_text"
+ android:layout_marginStart="6dp"
+ android:scaleType="centerCrop"/>
<TextView
android:id="@+id/application_name"
@@ -53,8 +54,7 @@
android:lineHeight="20dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
+ android:layout_marginStart="8dp"
tools:text="Gmail"/>
<ImageButton
@@ -67,6 +67,7 @@
android:scaleType="fitCenter"
android:clickable="false"
android:focusable="false"
+ android:layout_marginHorizontal="8dp"
android:layout_gravity="center_vertical"/>
</LinearLayout>
@@ -87,14 +88,15 @@
<ImageButton
android:id="@+id/close_window"
- android:layout_width="40dp"
+ android:layout_width="44dp"
android:layout_height="40dp"
- android:padding="4dp"
+ android:paddingHorizontal="10dp"
+ android:paddingVertical="8dp"
android:layout_marginEnd="8dp"
android:tint="?androidprv:attr/materialColorOnSurface"
android:background="?android:selectableItemBackgroundBorderless"
android:contentDescription="@string/close_button_text"
- android:src="@drawable/decor_close_button_dark"
- android:scaleType="fitCenter"
+ android:src="@drawable/desktop_mode_header_ic_close"
+ android:scaleType="centerCrop"
android:gravity="end"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 8baaf2f155af..a541c590575f 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -145,4 +145,7 @@
<!-- Whether CompatUIController is enabled -->
<bool name="config_enableCompatUIController">true</bool>
+
+ <!-- Whether pointer pilfer is required to start back animation. -->
+ <bool name="config_backAnimationRequiresPointerPilfer">true</bool>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 48e6428524ae..7dd39613b438 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -434,15 +434,22 @@
<!-- (32 dp buttons + 10dp margins) * 3 buttons-->
<dimen name="caption_right_buttons_width">126dp</dimen>
- <!-- 2 buttons * 48dp button size. -->
- <dimen name="desktop_mode_right_edge_buttons_width">96dp</dimen>
+ <!-- 2 buttons * 44dp button size + 16dp total margins. -->
+ <dimen name="desktop_mode_right_edge_buttons_width">104dp</dimen>
<!-- 22dp padding + 24dp app icon + 16dp expand button.
Text varies in size, we will calculate that width separately. -->
<dimen name="desktop_mode_app_details_width_minus_text">62dp</dimen>
- <!-- 22dp padding + 24dp app icon + 16dp expand button + 86dp text (max) -->
- <dimen name="desktop_mode_app_details_max_width">148dp</dimen>
+ <!-- When custom headers are requested, this is the width of the left-aligned region that is
+ taken up by caption elements and extra margins. The customizable region starts at the
+ end of this area. -->
+ <dimen name="desktop_mode_customizable_caption_margin_start">84dp</dimen>
+
+ <!-- When custom headers are requested, this is the width of the right-aligned region that is
+ taken up by caption elements and extra margins. The customizable region ends at the
+ start of this area. -->
+ <dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen>
<!-- The width of the maximize menu in desktop mode. -->
<dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
@@ -490,7 +497,7 @@
<dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen>
<!-- The radius of the caption menu icon. -->
- <dimen name="desktop_mode_caption_icon_radius">28dp</dimen>
+ <dimen name="desktop_mode_caption_icon_radius">24dp</dimen>
<!-- The radius of the caption menu shadow. -->
<dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
@@ -503,10 +510,6 @@
split select if dragged until the touch input is within the range. -->
<dimen name="desktop_mode_transition_area_width">32dp</dimen>
- <!-- The height of the area at the top of the screen where a freeform task will transition to
- fullscreen if dragged until the top bound of the task is within the area. -->
- <dimen name="desktop_mode_transition_area_height">16dp</dimen>
-
<!-- The width of the area where a desktop task will transition to fullscreen. -->
<dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
index 93893e33d2d5..ef9bf008b294 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -51,7 +51,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio
final ILogger logger = pw::println;
switch (args[0]) {
case "status": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
return false;
}
@@ -59,7 +59,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio
return true;
}
case "start": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
return false;
}
@@ -67,7 +67,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio
return true;
}
case "stop": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
return false;
}
@@ -101,7 +101,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio
return mShellProtoLog.stopLoggingToLogcat(groups, logger) == 0;
}
case "save-for-bugreport": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command");
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 2606fb661e80..9bd8531d33dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -64,6 +64,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -115,6 +116,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private boolean mShouldStartOnNextMoveEvent = false;
private boolean mOnBackStartDispatched = false;
private boolean mPointerPilfered = false;
+ private final boolean mRequirePointerPilfer;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -220,6 +222,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActivityTaskManager = activityTaskManager;
mContext = context;
mContentResolver = contentResolver;
+ mRequirePointerPilfer =
+ context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer);
mBgHandler = bgHandler;
shellInit.addInitCallback(this::onInit, this);
mAnimationBackground = backAnimationBackground;
@@ -560,7 +564,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private void tryDispatchOnBackStarted(
IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
- if (mOnBackStartDispatched || callback == null || !mPointerPilfered) {
+ if (mOnBackStartDispatched
+ || callback == null
+ || (!mPointerPilfered && mRequirePointerPilfer)) {
return;
}
dispatchOnBackStarted(callback, backEvent);
@@ -1006,6 +1012,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted);
pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
+ pw.println(prefix + " mPointerPilfered=" + mPointerPilfered);
+ pw.println(prefix + " mRequirePointerPilfer=" + mRequirePointerPilfer);
pw.println(prefix + " mCurrentTracker state:");
mCurrentTracker.dump(pw, prefix + " ");
pw.println(prefix + " mQueuedTracker state:");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 8fd6ffe15cfe..474430eb44ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -717,11 +717,6 @@ public class BubbleStackView extends FrameLayout
// Hide the stack after a delay, if needed.
updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
-
- if (mShouldReorderBubblesAfterGestureCompletes) {
- mShouldReorderBubblesAfterGestureCompletes = false;
- updateBubbleOrderInternal(mBubbleData.getBubbles(), true);
- }
}
};
@@ -2732,6 +2727,12 @@ public class BubbleStackView extends FrameLayout
ev.getAction() != MotionEvent.ACTION_UP
&& ev.getAction() != MotionEvent.ACTION_CANCEL;
+ // If there is a deferred reorder action, and the gesture is over, run it now.
+ if (mShouldReorderBubblesAfterGestureCompletes && !mIsGestureInProgress) {
+ mShouldReorderBubblesAfterGestureCompletes = false;
+ updateBubbleOrderInternal(mBubbleData.getBubbles(), false);
+ }
+
return dispatched;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 2ea43162d225..ad01d0fa311a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -20,12 +20,12 @@ import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_CANCEL;
import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_END;
import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_START;
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
-import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.res.Configuration;
@@ -51,6 +51,7 @@ import android.view.inputmethod.InputMethodManagerGlobal;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
@@ -122,7 +123,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
- pd.startAnimation(true, false /* forceRestart */, null /* statsToken */);
+ pd.startAnimation(true, false /* forceRestart */,
+ SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED);
}
}
@@ -257,7 +259,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mInsetsState.set(insetsState, true /* copySources */);
if (mImeShowing && !Objects.equals(oldFrame, newFrame) && newSourceVisible) {
if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
- startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
+ startAnimation(mImeShowing, true /* forceRestart */,
+ SoftInputShowHideReason.DISPLAY_INSETS_CHANGED);
}
}
@@ -291,7 +294,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
final boolean positionChanged =
!imeSourceControl.getSurfacePosition().equals(lastSurfacePosition);
if (positionChanged) {
- startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
+ startAnimation(mImeShowing, true /* forceRestart */,
+ SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED);
}
} else {
if (!haveSameLeash(mImeSourceControl, imeSourceControl)) {
@@ -384,7 +388,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
private void startAnimation(final boolean show, final boolean forceRestart,
- @Nullable ImeTracker.Token statsToken) {
+ @SoftInputShowHideReason int reason) {
+ final var imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
+ if (imeSource == null || mImeSourceControl == null) {
+ return;
+ }
+ final var statsToken = ImeTracker.forLogging().onStart(
+ show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_WM_SHELL,
+ reason, false /* fromUser */);
+
+ startAnimation(show, forceRestart, statsToken);
+ }
+
+ private void startAnimation(final boolean show, final boolean forceRestart,
+ @NonNull final ImeTracker.Token statsToken) {
final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
if (imeSource == null || mImeSourceControl == null) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
@@ -458,7 +475,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
- @Nullable
+ @NonNull
private final ImeTracker.Token mStatsToken = statsToken;
@Override
@@ -484,7 +501,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
- statsToken != null ? statsToken.getTag() : TOKEN_NONE,
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, alpha, startY , endY,
Objects.toString(mImeSourceControl.getLeash()),
Objects.toString(mImeSourceControl.getInsetsHint()),
@@ -500,7 +517,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mCancelled = true;
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL,
- statsToken != null ? statsToken.getTag() : TOKEN_NONE, mDisplayId,
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mDisplayId,
Objects.toString(mImeSourceControl.getInsetsHint()));
}
}
@@ -528,7 +546,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
- statsToken != null ? statsToken.getTag() : TOKEN_NONE,
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, endY,
Objects.toString(mImeSourceControl.getLeash()),
Objects.toString(mImeSourceControl.getInsetsHint()),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
index 9bdda14cf00b..ca06024a9adb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -277,8 +277,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
*
* @param types {@link InsetsType} to show
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME show request
- * or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void showInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {}
@@ -288,8 +287,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
*
* @param types {@link InsetsType} to hide
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME hide request
- * or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void hideInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index f801b0d01084..a87116ea4670 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -75,7 +75,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
private SurfaceControlViewHost mViewHost;
private DividerHandleView mHandle;
private DividerRoundedCorner mCorners;
- private View mBackground;
private int mTouchElevation;
private VelocityTracker mVelocityTracker;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS
index 7237d2bde39f..37ccd15587e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS
@@ -1,2 +1,4 @@
# WM shell sub-modules splitscreen owner
chenghsiuchang@google.com
+jeremysim@google.com
+peanutbutter@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 2b1037711249..dae62ac74483 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -138,8 +138,10 @@ public class SplitDecorManager extends WindowlessWindowManager {
mViewHost.setView(rootLayout, lp);
}
- /** Releases the surfaces for split decor. */
- public void release(SurfaceControl.Transaction t) {
+ /**
+ * Cancels any currently running animations.
+ */
+ public void cancelRunningAnimations() {
if (mFadeAnimator != null) {
if (mFadeAnimator.isRunning()) {
mFadeAnimator.cancel();
@@ -152,6 +154,11 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
mScreenshotAnimator = null;
}
+ }
+
+ /** Releases the surfaces for split decor. */
+ public void release(SurfaceControl.Transaction t) {
+ cancelRunningAnimations();
if (mViewHost != null) {
mViewHost.release();
mViewHost = null;
@@ -277,7 +284,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
@Override
- public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
+ public void onAnimationEnd(@NonNull Animator animation) {
mRunningAnimationCount--;
animT.remove(mScreenshot);
animT.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 53caddb52f23..6b2d544c192a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -510,16 +510,18 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
}
- /** Updates divide position and split bounds base on the ratio within root bounds. */
+ /**
+ * Updates divide position and split bounds base on the ratio within root bounds. Falls back
+ * to middle position if the provided SnapTarget is not supported.
+ */
public void setDivideRatio(@PersistentSnapPosition int snapPosition) {
final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget(
snapPosition);
- if (snapTarget == null) {
- throw new IllegalArgumentException("No SnapTarget for position " + snapPosition);
- }
-
- setDividePosition(snapTarget.position, false /* applyLayoutChange */);
+ setDividePosition(snapTarget != null
+ ? snapTarget.position
+ : mDividerSnapAlgorithm.getMiddleTarget().position,
+ false /* applyLayoutChange */);
}
/** Resets divider position. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index f757e1c88cb8..fb3c35b6a1e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -201,9 +201,11 @@ public abstract class WMShellModule {
@Provides
static WindowDecorViewModel provideWindowDecorViewModel(
Context context,
+ @ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellMainThread Choreographer mainChoreographer,
ShellInit shellInit,
+ IWindowManager windowManager,
ShellCommandHandler shellCommandHandler,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
@@ -216,10 +218,12 @@ public abstract class WMShellModule {
if (DesktopModeStatus.isEnabled()) {
return new DesktopModeWindowDecorViewModel(
context,
+ mainExecutor,
mainHandler,
mainChoreographer,
shellInit,
shellCommandHandler,
+ windowManager,
taskOrganizer,
displayController,
shellController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 7091c4b7210a..fb0ed1587055 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -98,6 +98,7 @@ public class DesktopModeVisualIndicator {
* Based on the coordinates of the current drag event, determine which indicator type we should
* display, including no visible indicator.
*/
+ @NonNull
IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) {
final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
// If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
@@ -136,18 +137,18 @@ public class DesktopModeVisualIndicator {
Region calculateFullscreenRegion(DisplayLayout layout,
@WindowConfiguration.WindowingMode int windowingMode, int captionHeight) {
final Region region = new Region();
- int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+ ? 2 * layout.stableInsets().top
+ : mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height);
// A thin, short Rect at the top of the screen.
if (windowingMode == WINDOWING_MODE_FREEFORM) {
int fromFreeformWidth = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width);
- int fromFreeformHeight = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height);
region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2),
-captionHeight,
(layout.width() / 2) + (fromFreeformWidth / 2),
- fromFreeformHeight));
+ transitionHeight));
}
// A screen-wide, shorter Rect if the task is in fullscreen or split.
if (windowingMode == WINDOWING_MODE_FULLSCREEN
@@ -155,7 +156,7 @@ public class DesktopModeVisualIndicator {
region.union(new Rect(0,
-captionHeight,
layout.width(),
- edgeTransitionHeight));
+ transitionHeight));
}
return region;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b9d0342137c5..654409f4a637 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -62,7 +62,6 @@ import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.annotations.ExternalThread
import com.android.wm.shell.common.annotations.ShellMainThread
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
import com.android.wm.shell.draganddrop.DragAndDropController
@@ -141,7 +140,7 @@ class DesktopTasksController(
private val transitionAreaHeight
get() = context.resources.getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height
)
private val transitionAreaWidth
@@ -417,23 +416,9 @@ class DesktopTasksController(
splitScreenController.getStageOfTask(taskInfo.taskId),
EXIT_REASON_DESKTOP_MODE
)
- getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo ->
- wct.removeTask(otherTaskInfo.token)
- }
}
}
- private fun getOtherSplitTask(taskId: Int): RunningTaskInfo? {
- val remainingTaskPosition: Int =
- if (splitScreenController.getSplitPosition(taskId)
- == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
- SPLIT_POSITION_TOP_OR_LEFT
- } else {
- SPLIT_POSITION_BOTTOM_OR_RIGHT
- }
- return splitScreenController.getTaskInfo(remainingTaskPosition)
- }
-
/**
* The second part of the animated drag to desktop transition, called after
* [startDragToDesktop].
@@ -580,30 +565,7 @@ class DesktopTasksController(
* @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
*/
fun snapToHalfScreen(taskInfo: RunningTaskInfo, position: SnapPosition) {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
-
- val stableBounds = Rect()
- displayLayout.getStableBounds(stableBounds)
-
- val destinationWidth = stableBounds.width() / 2
- val destinationBounds = when (position) {
- SnapPosition.LEFT -> {
- Rect(
- stableBounds.left,
- stableBounds.top,
- stableBounds.left + destinationWidth,
- stableBounds.bottom
- )
- }
- SnapPosition.RIGHT -> {
- Rect(
- stableBounds.right - destinationWidth,
- stableBounds.top,
- stableBounds.right,
- stableBounds.bottom
- )
- }
- }
+ val destinationBounds = getSnapBounds(taskInfo, position)
if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
@@ -624,8 +586,35 @@ class DesktopTasksController(
outBounds.set(0, 0, desiredWidth, desiredHeight)
// Center the task in screen bounds
outBounds.offset(
- screenBounds.centerX() - outBounds.centerX(),
- screenBounds.centerY() - outBounds.centerY())
+ screenBounds.centerX() - outBounds.centerX(),
+ screenBounds.centerY() - outBounds.centerY())
+ }
+
+ private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect()
+
+ val stableBounds = Rect()
+ displayLayout.getStableBounds(stableBounds)
+
+ val destinationWidth = stableBounds.width() / 2
+ return when (position) {
+ SnapPosition.LEFT -> {
+ Rect(
+ stableBounds.left,
+ stableBounds.top,
+ stableBounds.left + destinationWidth,
+ stableBounds.bottom
+ )
+ }
+ SnapPosition.RIGHT -> {
+ Rect(
+ stableBounds.right - destinationWidth,
+ stableBounds.top,
+ stableBounds.right,
+ stableBounds.bottom
+ )
+ }
+ }
}
/**
@@ -661,7 +650,7 @@ class DesktopTasksController(
?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) }
}
- private fun releaseVisualIndicator() {
+ fun releaseVisualIndicator() {
val t = SurfaceControl.Transaction()
visualIndicator?.releaseVisualIndicator(t)
visualIndicator = null
@@ -942,16 +931,13 @@ class DesktopTasksController(
taskSurface: SurfaceControl,
inputX: Float,
taskTop: Float
- ) {
+ ): DesktopModeVisualIndicator.IndicatorType {
// If the visual indicator does not exist, create it.
- if (visualIndicator == null) {
- visualIndicator = DesktopModeVisualIndicator(
- syncQueue, taskInfo, displayController, context, taskSurface,
- rootTaskDisplayAreaOrganizer)
- }
- // Then, update the indicator type.
- val indicator = visualIndicator ?: return
- indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
+ val indicator = visualIndicator ?: DesktopModeVisualIndicator(
+ syncQueue, taskInfo, displayController, context, taskSurface,
+ rootTaskDisplayAreaOrganizer)
+ if (visualIndicator == null) visualIndicator = indicator
+ return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
}
/**
@@ -971,20 +957,28 @@ class DesktopTasksController(
if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) {
return
}
- if (taskBounds.top <= transitionAreaHeight) {
- moveToFullscreenWithAnimation(taskInfo, position)
- return
- }
- if (inputCoordinate.x <= transitionAreaWidth) {
- releaseVisualIndicator()
- snapToHalfScreen(taskInfo, SnapPosition.LEFT)
- return
- }
- if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width()
- ?.minus(transitionAreaWidth) ?: return)) {
- releaseVisualIndicator()
- snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
- return
+
+ val indicator = visualIndicator ?: return
+ val indicatorType = indicator.updateIndicatorType(
+ PointF(inputCoordinate.x, taskBounds.top.toFloat()),
+ taskInfo.windowingMode
+ )
+ when (indicatorType) {
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+ moveToFullscreenWithAnimation(taskInfo, position)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+ releaseVisualIndicator()
+ snapToHalfScreen(taskInfo, SnapPosition.LEFT)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+ releaseVisualIndicator()
+ snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> {
+ releaseVisualIndicator()
+ }
}
// A freeform drag-move ended, remove the indicator immediately.
releaseVisualIndicator()
@@ -997,14 +991,28 @@ class DesktopTasksController(
* @param y height of drag, to be checked against status bar height.
*/
fun onDragPositioningEndThroughStatusBar(
+ inputCoordinates: PointF,
taskInfo: RunningTaskInfo,
freeformBounds: Rect
) {
- finalizeDragToDesktop(taskInfo, freeformBounds)
- }
-
- private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int {
- return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
+ val indicator = visualIndicator ?: return
+ val indicatorType = indicator
+ .updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
+ when (indicatorType) {
+ DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
+ finalizeDragToDesktop(taskInfo, freeformBounds)
+ }
+ DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+ cancelDragToDesktop(taskInfo)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+ finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT))
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+ finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT))
+ }
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index c7daf561f682..87e372cc304c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -63,6 +63,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.util.Rational;
import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
@@ -126,6 +127,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
SystemProperties.getInt(
"persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400);
+ private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.005f;
+
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -767,6 +770,37 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPictureInPictureParams.getTitle());
mPipParamsChangedForwarder.notifySubtitleChanged(
mPictureInPictureParams.getSubtitle());
+
+ if (mPictureInPictureParams.hasSourceBoundsHint()
+ && mPictureInPictureParams.hasSetAspectRatio()) {
+ Rational sourceRectHintAspectRatio = new Rational(
+ mPictureInPictureParams.getSourceRectHint().width(),
+ mPictureInPictureParams.getSourceRectHint().height());
+ if (sourceRectHintAspectRatio.compareTo(
+ mPictureInPictureParams.getAspectRatio()) != 0) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "Aspect ratio of source rect hint (%d/%d) does not match the provided "
+ + "aspect ratio value (%d/%d). Consider matching them for "
+ + "improved animation. Future releases might override the "
+ + "value to match.",
+ mPictureInPictureParams.getSourceRectHint().width(),
+ mPictureInPictureParams.getSourceRectHint().height(),
+ mPictureInPictureParams.getAspectRatio().getNumerator(),
+ mPictureInPictureParams.getAspectRatio().getDenominator());
+ }
+ if (Math.abs(sourceRectHintAspectRatio.floatValue()
+ - mPictureInPictureParams.getAspectRatioFloat())
+ > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "Aspect ratio of source rect hint (%f) does not match the provided "
+ + "aspect ratio value (%f) and is above threshold of %f. "
+ + "Consider matching them for improved animation. Future "
+ + "releases might override the value to match.",
+ sourceRectHintAspectRatio.floatValue(),
+ mPictureInPictureParams.getAspectRatioFloat(),
+ PIP_ASPECT_RATIO_MISMATCH_THRESHOLD);
+ }
+ }
}
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 2cdec81d77ac..4d47ca998d8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -122,6 +122,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private static final long PIP_KEEP_CLEAR_AREAS_DELAY =
SystemProperties.getLong("persist.wm.debug.pip_keep_clear_areas_delay", 200);
+ private static final long ENABLE_TOUCH_DELAY_MS = 200L;
+
private Context mContext;
protected ShellExecutor mMainExecutor;
private DisplayController mDisplayController;
@@ -152,6 +154,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback =
this::onKeepClearAreasChangedCallback;
+ private final Runnable mEnableTouchCallback = () -> mTouchHandler.setTouchEnabled(true);
+
private void onKeepClearAreasChangedCallback() {
if (mIsKeyguardShowingOrAnimating) {
// early bail out if the change was caused by keyguard showing up
@@ -1037,6 +1041,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
saveReentryState(pipBounds);
}
// Disable touches while the animation is running
+ mMainExecutor.removeCallbacks(mEnableTouchCallback);
mTouchHandler.setTouchEnabled(false);
if (mPinnedStackAnimationRecentsCallback != null) {
mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
@@ -1067,7 +1072,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
InteractionJankMonitor.getInstance().end(CUJ_PIP_TRANSITION);
// Re-enable touches after the animation completes
- mTouchHandler.setTouchEnabled(true);
+ mMainExecutor.executeDelayed(mEnableTouchCallback, ENABLE_TOUCH_DELAY_MS);
mTouchHandler.onPinnedStackAnimationEnded(direction);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index e4213569b526..1c54754e9953 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -163,14 +163,14 @@ public class RecentTasksController implements TaskStackListenerCallback,
/**
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
*/
- public void addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
+ public boolean addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
if (taskId1 == taskId2) {
- return;
+ return false;
}
if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2
&& mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) {
// If the two tasks are already paired and the bounds are the same, then skip updating
- return;
+ return false;
}
// Remove any previous pairs
removeSplitPair(taskId1);
@@ -185,6 +185,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
notifyRecentTasksChanged();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s",
taskId1, taskId2, splitBounds);
+ return true;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index e52235fda80f..64e26dbd70be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -16,11 +16,14 @@
package com.android.wm.shell.splitscreen;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -50,6 +53,8 @@ class MainStage extends StageTaskListener {
void activate(WindowContainerTransaction wct, boolean includingTopTask) {
if (mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b",
+ includingTopTask);
if (includingTopTask) {
reparentTopTask(wct);
@@ -64,6 +69,8 @@ class MainStage extends StageTaskListener {
void deactivate(WindowContainerTransaction wct, boolean toTop) {
if (!mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s",
+ toTop, mRootTaskInfo);
mIsActive = false;
if (mRootTaskInfo == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS
index 7237d2bde39f..37ccd15587e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS
@@ -1,2 +1,4 @@
# WM shell sub-modules splitscreen owner
chenghsiuchang@google.com
+jeremysim@google.com
+peanutbutter@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 9903113c5453..f5fbae55960a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,12 +16,15 @@
package com.android.wm.shell.splitscreen;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
import android.app.ActivityManager;
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -47,6 +50,8 @@ class SideStage extends StageTaskListener {
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
+ mChildrenTaskInfo.size(), toTop);
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
mRootTaskInfo.token,
@@ -59,6 +64,8 @@ class SideStage extends StageTaskListener {
boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
+ task != null);
if (task == null) return false;
wct.reparent(task.token, newParent, false /* onTop */);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index b60e361caad8..1a53a1d10dd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -25,6 +25,8 @@ import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
@@ -101,6 +103,7 @@ class SplitScreenTransitions {
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playAnimation: transition=%d", info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
final TransitSession pendingTransition = getPendingTransition(transition);
@@ -123,10 +126,12 @@ class SplitScreenTransitions {
playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
}
- /** Internal funcation of playAnimation. */
+ /** Internal function of playAnimation. */
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
@NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playInternalAnimation: transition=%d",
+ info.getDebugId());
// Play some place-holder fade animations
final boolean isEnter = isPendingEnter(transition);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -220,6 +225,8 @@ class SplitScreenTransitions {
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor,
@NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playDragDismissAnimation: transition=%d",
+ info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -259,6 +266,7 @@ class SplitScreenTransitions {
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -312,13 +320,15 @@ class SplitScreenTransitions {
@Nullable
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved enter transition");
return mPendingEnter;
} else if (isPendingDismiss(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved dismiss transition");
return mPendingDismiss;
} else if (isPendingResize(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
return mPendingResize;
}
-
return null;
}
@@ -339,7 +349,7 @@ class SplitScreenTransitions {
Transitions.TransitionHandler handler,
int extraTransitType, boolean resizeAnim) {
if (mPendingEnter != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start enter split transition since it already exist. ");
return null;
}
@@ -355,8 +365,10 @@ class SplitScreenTransitions {
mPendingEnter = new EnterSession(
transition, remoteTransition, extraTransitType, resizeAnim);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter split screen");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setEnterTransition: transitType=%d resize=%b",
+ extraTransitType, resizeAnim);
}
/** Starts a transition to dismiss split. */
@@ -364,7 +376,7 @@ class SplitScreenTransitions {
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@SplitScreenController.ExitReason int reason) {
if (mPendingDismiss != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start dismiss split transition since it already exist. reason to "
+ " dismiss = %s", exitReasonToString(reason));
return null;
@@ -381,32 +393,33 @@ class SplitScreenTransitions {
@SplitScreenController.ExitReason int reason) {
mPendingDismiss = new DismissSession(transition, reason, dismissTop);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Dismiss due to %s. toTop=%s",
exitReasonToString(reason), stageTypeToString(dismissTop));
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setDismissTransition: reason=%s dismissTop=%s",
+ exitReasonToString(reason), stageTypeToString(dismissTop));
}
IBinder startResizeTransition(WindowContainerTransaction wct,
Transitions.TransitionHandler handler,
@Nullable TransitionConsumedCallback consumedCallback,
- @Nullable TransitionFinishedCallback finishCallback) {
+ @Nullable TransitionFinishedCallback finishCallback,
+ @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " splitTransition deduced Resize split screen.");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition: hasPendingResize=%b",
+ mPendingResize != null);
if (mPendingResize != null) {
+ mainDecor.cancelRunningAnimations();
+ sideDecor.cancelRunningAnimations();
mPendingResize.cancel(null);
mAnimations.clear();
onFinish(null /* wct */);
}
IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler);
- setResizeTransition(transition, consumedCallback, finishCallback);
- return transition;
- }
-
- void setResizeTransition(@NonNull IBinder transition,
- @Nullable TransitionConsumedCallback consumedCallback,
- @Nullable TransitionFinishedCallback finishCallback) {
mPendingResize = new TransitSession(transition, consumedCallback, finishCallback);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
- + " deduced Resize split screen");
+ return transition;
}
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
@@ -444,12 +457,15 @@ class SplitScreenTransitions {
mPendingEnter.onConsumed(aborted);
mPendingEnter = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for enter transition");
} else if (isPendingDismiss(transition)) {
mPendingDismiss.onConsumed(aborted);
mPendingDismiss = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for dismiss transition");
} else if (isPendingResize(transition)) {
mPendingResize.onConsumed(aborted);
mPendingResize = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
}
// TODO: handle transition consumed for active remote handler
@@ -462,12 +478,15 @@ class SplitScreenTransitions {
if (isPendingEnter(mAnimatingTransition)) {
mPendingEnter.onFinished(wct, mFinishTransaction);
mPendingEnter = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for enter transition");
} else if (isPendingDismiss(mAnimatingTransition)) {
mPendingDismiss.onFinished(wct, mFinishTransaction);
mPendingDismiss = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for dismiss transition");
} else if (isPendingResize(mAnimatingTransition)) {
mPendingResize.onFinished(wct, mFinishTransaction);
mPendingResize = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
}
mActiveRemoteHandler = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2933cf48614a..76504447339f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -44,6 +44,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
@@ -54,6 +55,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASO
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
@@ -138,6 +140,7 @@ import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.splitscreen.SplitScreenController.SplitEnterReason;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -313,6 +316,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
mMainStage = new MainStage(
mContext,
mTaskOrganizer,
@@ -454,6 +458,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveToStage: task=%d position=%d", task.taskId,
+ stagePosition);
prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */);
if (ENABLE_SHELL_TRANSITIONS) {
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
@@ -474,6 +480,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
boolean removeFromSideStage(int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
/**
@@ -498,11 +505,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo, splitPosition,
taskBounds);
}
- if (enteredSplitSelect) mTaskOrganizer.applyTransaction(wct);
+ if (enteredSplitSelect) {
+ mTaskOrganizer.applyTransaction(wct);
+ }
}
void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
Bundle options, UserHandle user) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startShortcut: pkg=%s id=%s position=%d user=%d",
+ packageName, shortcutId, position, user.getIdentifier());
final boolean isEnteringSplit = !isSplitActive();
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -564,6 +575,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Use this method to launch an existing Task via a taskId */
void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position);
mSplitRequest = new SplitRequest(taskId, position);
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
@@ -595,6 +607,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Launches an activity into split. */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(),
+ position);
mSplitRequest = new SplitRequest(intent.getIntent(), position);
if (!ENABLE_SHELL_TRANSITIONS) {
startIntentLegacy(intent, fillInIntent, position, options);
@@ -690,6 +704,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startTasks: task1=%d task2=%d position=%d snapPosition=%d",
+ taskId1, taskId2, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId2 == INVALID_TASK_ID) {
if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
@@ -718,6 +735,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d",
+ pendingIntent.getIntent(), taskId, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -740,6 +760,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
@PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startShortcutAndTask: shortcut=%s task1=%d position=%d snapPosition=%d",
+ shortcutInfo, taskId, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -801,6 +824,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startIntents: intent1=%s intent2=%s position=%d snapPosition=%d",
+ pendingIntent1.getIntent(), pendingIntent2.getIntent(), splitPosition,
+ snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (pendingIntent2 == null) {
options1 = options1 != null ? options1 : new Bundle();
@@ -1302,6 +1329,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
void switchSplitPosition(String reason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "switchSplitPosition");
final SurfaceControl.Transaction t = mTransactionPool.acquire();
mTempRect1.setEmpty();
final StageTaskListener topLeftStage =
@@ -1343,7 +1371,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
});
});
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
+ ProtoLog.v(WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLeftRightSplit());
@@ -1376,11 +1404,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!mMainStage.isActive()) {
return;
}
-
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onKeyguardVisibilityChanged: showing=%b", showing);
setDividerVisibility(!mKeyguardShowing, null);
}
void onFinishedWakingUp() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinishedWakingUp");
if (!mMainStage.isActive()) {
return;
}
@@ -1421,6 +1450,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: topTaskId=%d reason=%s active=%b",
+ toTopTaskId, exitReasonToString(exitReason), mMainStage.isActive());
if (!mMainStage.isActive()) return;
StageTaskListener childrenToTop = null;
@@ -1439,6 +1470,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
@ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b",
+ childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive());
if (!mMainStage.isActive()) return;
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1447,6 +1480,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s",
+ exitReasonToString(exitReason));
if (!mMainStage.isActive() || mIsExiting) return;
onSplitScreenExit();
@@ -1502,7 +1537,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
});
- Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
// Log the exit
if (childrenToTop != null) {
logExitToStage(exitReason, childrenToTop == mMainStage);
@@ -1527,6 +1561,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* Exits the split screen by finishing one of the tasks.
*/
protected void exitStage(@SplitPosition int stageToClose) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitStage: stageToClose=%d", stageToClose);
mSplitLayout.flingDividerToDismiss(stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT,
EXIT_REASON_APP_FINISHED);
}
@@ -1540,12 +1575,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
try {
activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
} catch (RemoteException | NullPointerException e) {
- ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.e(WM_SHELL_SPLIT_SCREEN,
"Unable to update focus on the chosen stage: %s", e.getMessage());
}
}
private void clearRequestIfPresented() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
&& mMainStageListener.mVisible && mSideStageListener.mHasChildren) {
mSplitRequest = null;
@@ -1572,6 +1608,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// The device is folded
case EXIT_REASON_FULLSCREEN_SHORTCUT:
// User has used a keyboard shortcut to go back to fullscreen from split
+ case EXIT_REASON_DESKTOP_MODE:
+ // One of the children enters desktop mode
return true;
default:
return false;
@@ -1581,6 +1619,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void clearSplitPairedInRecents(@ExitReason int exitReason) {
if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: reason=%s",
+ exitReasonToString(exitReason));
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
@@ -1597,11 +1637,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen");
prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED,
!mIsDropEntering);
}
@@ -1613,6 +1655,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void prepareEnterSplitScreen(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen: position=%d resize=%b",
+ startPosition, resizeAnim);
onSplitScreenEnter();
// Preemptively reset the reparenting behavior if we know that we are entering, as starting
// split tasks with activity trampolines can inadvertently trigger the task to be
@@ -1629,6 +1673,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void prepareBringSplit(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareBringSplit: task=%d isSplitVisible=%b",
+ taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
if (taskInfo != null) {
wct.startTask(taskInfo.taskId,
resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct));
@@ -1649,6 +1695,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void prepareActiveSplit(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareActiveSplit: task=%d isSplitVisible=%b",
+ taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
if (!ENABLE_SHELL_TRANSITIONS) {
// Legacy transition we need to create divider here, shell transition case we will
// create it on #finishEnterSplitScreen
@@ -1667,6 +1715,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareSplitLayout: resize=%b", resizeAnim);
if (resizeAnim) {
mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
} else {
@@ -1686,6 +1735,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
mSplitLayout.update(finishT, true /* resetImePosition */);
mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
getMainStageBounds());
@@ -1835,12 +1885,20 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
- recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
+ boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId,
+ splitBounds);
+ if (added) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "updateRecentTasksSplitPair: adding split pair ltTask=%d rbTask=%d",
+ leftTopTaskId, rightBottomTaskId);
+ }
}
});
}
private void sendSplitVisibilityChanged() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "sendSplitVisibilityChanged: dividerVisible=%b",
+ mDividerVisible);
for (int i = mListeners.size() - 1; i >= 0; --i) {
final SplitScreen.SplitScreenListener l = mListeners.get(i);
l.onSplitVisibilityChanged(mDividerVisible);
@@ -1855,6 +1913,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
throw new IllegalArgumentException(this + "\n Unknown task appeared: " + taskInfo);
}
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%s", taskInfo);
mRootTaskInfo = taskInfo;
mRootTaskLeash = leash;
@@ -1880,6 +1939,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
&& mMainStage.isActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating",
+ taskInfo.taskId);
// Clear the divider remote animating flag as the divider will be re-rendered to apply
// the new rotation config. Don't reset the IME state since those updates are not in
// sync with task info changes.
@@ -1892,6 +1953,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
@CallSuper
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%s", taskInfo);
if (mRootTaskInfo == null) {
throw new IllegalArgumentException(this + "\n Unknown task vanished: " + taskInfo);
}
@@ -1911,6 +1973,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@VisibleForTesting
void onRootTaskAppeared() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
+ mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask);
// Wait unit all root tasks appeared.
if (mRootTaskInfo == null
|| !mMainStageListener.mHasRootTask
@@ -1937,6 +2001,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* #onStageHasChildrenChanged because this would be called every time child task appeared.
* NOTICE: This only be called on legacy transition. */
private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d",
+ stageListener == mMainStageListener, taskId);
// Handle entering split screen while there is a split pair running in the background.
if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
&& mSplitRequest == null) {
@@ -1960,6 +2026,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
private void onRootTaskVanished() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished");
final WindowContainerTransaction wct = new WindowContainerTransaction();
mLaunchAdjacentController.clearLaunchAdjacentRoot();
applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED);
@@ -1990,6 +2057,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return;
}
+ // TODO Protolog
+
// Check if it needs to dismiss split screen when both stage invisible.
if (!mainStageVisible && mExitSplitScreenOnHide) {
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
@@ -2020,14 +2089,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return;
}
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Request to %s divider bar from %s.",
- (visible ? "show" : "hide"), Debug.getCaller());
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "setDividerVisibility: visible=%b keyguardShowing=%b dividerAnimating=%b caller=%s",
+ visible, mKeyguardShowing, mIsDividerRemoteAnimating, Debug.getCaller());
// Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
// dismissing animation.
if (visible && mKeyguardShowing) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Defer showing divider bar due to keyguard showing.");
return;
}
@@ -2036,7 +2105,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
sendSplitVisibilityChanged();
if (mIsDividerRemoteAnimating) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
}
@@ -2050,12 +2119,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void applyDividerVisibility(@Nullable SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to divider leash not ready.");
return;
}
if (mIsDividerRemoteAnimating) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
}
@@ -2119,6 +2188,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Callback when split roots have child or haven't under it.
* NOTICE: This only be called on legacy transition. */
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b",
+ stageListener == mMainStageListener);
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
@@ -2170,13 +2241,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
- public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
+ public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s",
+ bottomOrRight, exitReasonToString(exitReason));
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
if (!ENABLE_SHELL_TRANSITIONS) {
- exitSplitScreen(toTopStage, reason);
+ exitSplitScreen(toTopStage, exitReason);
return;
}
@@ -2219,6 +2292,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onLayoutSizeChanged(SplitLayout layout) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onLayoutSizeChanged");
// Reset this flag every time onLayoutSizeChanged.
mShowDecorImmediately = false;
@@ -2237,10 +2311,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (ENABLE_SHELL_TRANSITIONS) {
mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart");
mSplitTransitions.startResizeTransition(wct, this, (aborted) -> {
- mSplitLayout.setDividerInteractive(true, false, "onSplitResizeConsumed");
- }, (finishWct, t) -> {
- mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish");
- });
+ mSplitLayout.setDividerInteractive(true, false, "onSplitResizeConsumed");
+ }, (finishWct, t) -> {
+ mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish");
+ }, mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager());
} else {
// Only need screenshot for legacy case because shell transition should screenshot
// itself during transition.
@@ -2278,8 +2352,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
+ boolean updated = layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
bottomRightStage.mRootTaskInfo);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateWindowBounds: topLeftStage=%s bottomRightStage=%s",
+ layout.getBounds1(), layout.getBounds2());
+ return updated;
}
void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
@@ -2291,6 +2368,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
(layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
applyResizingOffset);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "updateSurfaceBounds: topLeftStage=%s bottomRightStage=%s",
+ layout.getBounds1(), layout.getBounds2());
}
@Override
@@ -2329,6 +2409,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLayoutOffsetTarget: x=%d y=%d",
+ offsetX, offsetY);
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
@@ -2343,6 +2425,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (displayId != DEFAULT_DISPLAY) {
return;
}
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDisplayAdded: display=%d", displayId);
mDisplayController.addDisplayChangingController(this::onDisplayChange);
}
@@ -2357,8 +2440,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
- if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return;
+ if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) {
+ return;
+ }
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onDisplayChange: display=%d fromRot=%d toRot=%d config=%s",
+ displayId, fromRotation, toRotation,
+ newDisplayAreaInfo != null ? newDisplayAreaInfo.configuration : null);
mSplitLayout.rotateTo(toRotation);
if (newDisplayAreaInfo != null) {
mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration);
@@ -2369,6 +2458,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@VisibleForTesting
void onFoldedStateChanged(boolean folded) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFoldedStateChanged: folded=%b", folded);
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
if (!folded) return;
@@ -2439,6 +2529,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
if (isSplitActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
+ request.getDebugId());
// Check if the display is rotating.
final TransitionRequestInfo.DisplayChange displayChange =
request.getDisplayChange();
@@ -2467,6 +2559,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
if (isSplitActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d split active",
+ request.getDebugId());
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -2541,6 +2635,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return null;
} else {
if (isOpening && getStageOfTask(triggerTask) != null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split",
+ request.getDebugId());
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
@@ -2557,6 +2653,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
*/
public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request,
@NonNull WindowContainerTransaction outWCT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addEnterOrExitIfNeeded: transition=%d",
+ request.getDebugId());
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask != null && triggerTask.displayId != mDisplayId) {
// Skip handling task on the other display.
@@ -2591,6 +2689,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "mergeAnimation: transition=%d", info.getDebugId());
mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
}
@@ -2602,6 +2701,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed");
mSplitTransitions.onTransitionConsumed(transition, aborted, finishT);
}
@@ -2617,6 +2717,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// If we're not in split-mode, just abort so something else can handle it.
if (!mMainStage.isActive()) return false;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId());
mSplitLayout.setFreezeDividerWindow(false);
final StageChangeRecord record = new StageChangeRecord();
final int transitType = info.getType();
@@ -2727,6 +2828,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
startTransaction, finishTransaction, finishCallback)) {
if (mSplitTransitions.isPendingResize(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startAnimation: transition=%d display change", info.getDebugId());
// Only need to update in resize because divider exist before transition.
mSplitLayout.update(startTransaction, true /* resetImePosition */);
startTransaction.apply();
@@ -2797,6 +2900,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingAnimation: transition=%d",
+ info.getDebugId());
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(transition,
@@ -2830,6 +2935,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Called to clean-up state and do house-keeping after the animation is done. */
public void onTransitionAnimationComplete() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete");
// If still playing, let it finish.
if (!mMainStage.isActive() && !mIsExiting) {
// Update divider state after animation so that it is still around and positioned
@@ -2842,6 +2948,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@NonNull SplitScreenTransitions.EnterSession enterTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingEnterAnimation: enterTransition=%s",
+ enterTransition);
// First, verify that we actually have opened apps in both splits.
TransitionInfo.Change mainChild = null;
TransitionInfo.Change sideChild = null;
@@ -2959,6 +3067,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
public void goToFullscreenFromSplit() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "goToFullscreenFromSplit");
// If main stage is focused, toEnd = true if
// mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT. Otherwise toEnd = false
// If side stage is focused, toEnd = true if
@@ -2974,6 +3083,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Move the specified task to fullscreen, regardless of focus state. */
public void moveTaskToFullscreen(int taskId, int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveTaskToFullscreen");
boolean leftOrTop;
if (mMainStage.containsTask(taskId)) {
leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
@@ -2994,6 +3104,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
*/
public void onPipExpandToSplit(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onPipExpandToSplit: task=%s", taskInfo);
prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo),
false /*resizeAnim*/);
@@ -3040,6 +3151,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "prepareDismissAnimation: transition=%d toStage=%d reason=%s",
+ info.getDebugId(), toStage, exitReasonToString(dismissReason));
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
@@ -3126,6 +3240,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@NonNull SplitScreenTransitions.DismissSession dismissTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startPendingDismissAnimation: transition=%d dismissTransition=%s",
+ info.getDebugId(), dismissTransition);
prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
t, finishT);
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
@@ -3146,6 +3263,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Call this when starting the open-recents animation while split-screen is active. */
public void onRecentsInSplitAnimationStart(TransitionInfo info) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationStart: transition=%d",
+ info.getDebugId());
if (isSplitScreenVisible()) {
// Cache tasks on live tile.
for (int i = 0; i < info.getChanges().size(); ++i) {
@@ -3178,6 +3297,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Call this when the recents animation during split-screen finishes. */
public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish");
mPausingTasks.clear();
// Check if the recent transition is finished by returning to the current
// split, so we can restore the divider bar.
@@ -3203,6 +3323,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Call this when the recents animation finishes by doing pair-to-pair switch. */
public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish");
// Pair-to-pair switch happened so here should evict the live tile from its stage.
// Otherwise, the task will remain in stage, and occluding the new task when next time
// user entering recents.
@@ -3284,6 +3405,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* handled.
*/
private void setSplitsVisible(boolean visible) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible);
mMainStageListener.mVisible = mSideStageListener.mVisible = visible;
mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
}
@@ -3292,6 +3414,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* Sets drag info to be logged when splitscreen is next entered.
*/
public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDroppedToSplit: position=%d", position);
if (!isSplitScreenVisible()) {
mIsDropEntering = true;
mSkipEvictingMainStageChildren = true;
@@ -3308,7 +3431,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/**
* Sets info to be logged when splitscreen is next entered.
*/
- public void onRequestToSplit(InstanceId sessionId, int enterReason) {
+ public void onRequestToSplit(InstanceId sessionId, @SplitEnterReason int enterReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRequestToSplit: reason=%d", enterReason);
if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
// If split running background, exit split first.
// Skip this on shell transition due to we could evict existing tasks on transition
@@ -3384,6 +3508,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
if (mMainStage.isActive()) {
final boolean isMainStage = mMainStageListener == this;
if (!ENABLE_SHELL_TRANSITIONS) {
@@ -3393,7 +3518,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return;
}
- final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ // If visible, we preserve the app and keep it running. If an app becomes
+ // unsupported in the bg, break split without putting anything on top
+ boolean splitScreenVisible = isSplitScreenVisible();
+ int stageType = STAGE_TYPE_UNDEFINED;
+ if (splitScreenVisible) {
+ stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ }
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stageType, wct);
clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
@@ -3402,7 +3533,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow",
"app package " + taskInfo.baseActivity.getPackageName()
+ " does not support splitscreen, or is a controlled activity type"));
- mSplitUnsupportedToast.show();
+ if (splitScreenVisible) {
+ mSplitUnsupportedToast.show();
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index af7bf360f036..f33ab33dafcc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -25,6 +25,7 @@ import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
@@ -44,6 +45,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -175,6 +177,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
@CallSuper
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d",
+ taskInfo.taskId, taskInfo.parentTaskId,
+ mRootTaskInfo != null ? mRootTaskInfo.taskId : -1);
if (mRootTaskInfo == null) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
@@ -225,6 +230,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|| !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
|| !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
taskInfo.getWindowingMode())) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onTaskInfoChanged: task=%d no longer supports multiwindow",
+ taskInfo.taskId);
// Leave split screen if the task no longer supports multi window or have
// uncontrolled task.
mCallbacks.onNoLongerSupportMultiWindow(taskInfo);
@@ -251,6 +259,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
@CallSuper
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d", taskInfo.taskId);
final int taskId = taskInfo.taskId;
if (mRootTaskInfo.taskId == taskId) {
mCallbacks.onRootTaskVanished();
@@ -333,6 +342,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addTask: task=%d", task.taskId);
// Clear overridden bounds and windowing mode to make sure the child task can inherit
// windowing mode and bounds from split root.
wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
@@ -342,6 +352,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "reorderChild: task=%d onTop=%b", taskId, onTop);
if (!containsTask(taskId)) {
return;
}
@@ -357,6 +368,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
/** Collects all the current child tasks and prepares transaction to evict them to display. */
void evictAllChildren(WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evicting all children");
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
@@ -367,11 +379,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (taskId == taskInfo.taskId) continue;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict other child: task=%d", taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
void evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "evictNonOpeningChildren");
final SparseArray<ActivityManager.RunningTaskInfo> toBeEvict = mChildrenTaskInfo.clone();
for (int i = 0; i < apps.length; i++) {
if (apps[i].mode == MODE_OPENING) {
@@ -380,6 +394,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
for (int i = toBeEvict.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = toBeEvict.valueAt(i);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict non-opening child: task=%d", taskInfo.taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
@@ -388,12 +403,15 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (!taskInfo.isVisible) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict invisible child: task=%d",
+ taskInfo.taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
}
void evictChildren(WindowContainerTransaction wct, int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d", taskId);
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId);
if (taskInfo != null) {
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index bfb60c0f4fc8..da2965c05ee4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -417,7 +417,7 @@ public class SplashscreenContentDrawer {
final SplashViewBuilder builder = new SplashViewBuilder(context, ai);
final SplashScreenView view = builder
.setWindowBGColor(themeBGColor)
- .chooseStyle(STARTING_WINDOW_TYPE_SPLASH_SCREEN)
+ .chooseStyle(STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN)
.build();
view.setNotCopyable();
return view;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 96eaa1edbae4..91e9601c6a27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -190,6 +190,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
mRelayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition;
+ mRelayoutParams.mAllowCaptionInputFallthrough = false;
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index caa894fcbbc7..f4ccd689f938 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -30,6 +30,10 @@ import static android.view.WindowInsets.Type.statusBars;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION;
import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE;
@@ -48,9 +52,13 @@ import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.GestureDetector;
+import android.view.ISystemGestureExclusionListener;
+import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
@@ -73,9 +81,11 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
@@ -88,6 +98,7 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import java.io.PrintWriter;
import java.util.Optional;
@@ -102,6 +113,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private static final String TAG = "DesktopModeWindowDecorViewModel";
private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
+ private final IWindowManager mWindowManager;
+ private final ShellExecutor mMainExecutor;
private final ActivityTaskManager mActivityTaskManager;
private final ShellCommandHandler mShellCommandHandler;
private final ShellTaskOrganizer mTaskOrganizer;
@@ -111,7 +124,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
- private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final DesktopTasksController mDesktopTasksController;
+ private final InputManager mInputManager;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -119,8 +133,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final ExclusionRegionListener mExclusionRegionListener =
new ExclusionRegionListenerImpl();
- private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId =
- new SparseArray<>();
+ private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId;
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
private final InputMonitorFactory mInputMonitorFactory;
private TaskOperations mTaskOperations;
@@ -135,14 +148,31 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
new DesktopModeKeyguardChangeListener();
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final DisplayInsetsController mDisplayInsetsController;
+ private final Region mExclusionRegion = Region.obtain();
private boolean mInImmersiveMode;
+ private final ISystemGestureExclusionListener mGestureExclusionListener =
+ new ISystemGestureExclusionListener.Stub() {
+ @Override
+ public void onSystemGestureExclusionChanged(int displayId,
+ Region systemGestureExclusion, Region systemGestureExclusionUnrestricted) {
+ if (mContext.getDisplayId() != displayId) {
+ return;
+ }
+ mMainExecutor.execute(() -> {
+ mExclusionRegion.set(systemGestureExclusion);
+ });
+ }
+ };
+
public DesktopModeWindowDecorViewModel(
Context context,
+ ShellExecutor shellExecutor,
Handler mainHandler,
Choreographer mainChoreographer,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
+ IWindowManager windowManager,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
ShellController shellController,
@@ -154,10 +184,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
) {
this(
context,
+ shellExecutor,
mainHandler,
mainChoreographer,
shellInit,
shellCommandHandler,
+ windowManager,
taskOrganizer,
displayController,
shellController,
@@ -168,16 +200,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
- rootTaskDisplayAreaOrganizer);
+ rootTaskDisplayAreaOrganizer,
+ new SparseArray<>());
}
@VisibleForTesting
DesktopModeWindowDecorViewModel(
Context context,
+ ShellExecutor shellExecutor,
Handler mainHandler,
Choreographer mainChoreographer,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
+ IWindowManager windowManager,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
ShellController shellController,
@@ -188,8 +223,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId) {
mContext = context;
+ mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
@@ -199,12 +236,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDisplayInsetsController = displayInsetsController;
mSyncQueue = syncQueue;
mTransitions = transitions;
- mDesktopTasksController = desktopTasksController;
+ mDesktopTasksController = desktopTasksController.get();
mShellCommandHandler = shellCommandHandler;
+ mWindowManager = windowManager;
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ mInputManager = mContext.getSystemService(InputManager.class);
+ mWindowDecorByTaskId = windowDecorByTaskId;
shellInit.addInitCallback(this::onInit, this);
}
@@ -214,8 +254,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mShellCommandHandler.addDumpCallback(this::dump, this);
mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(),
new DesktopModeOnInsetsChangedListener());
- mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener(
- new DeskopModeOnTaskResizeAnimationListener()));
+ mDesktopTasksController.setOnTaskResizeAnimationListener(
+ new DeskopModeOnTaskResizeAnimationListener());
+ try {
+ mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener,
+ mContext.getDisplayId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register window manager callbacks", e);
+ }
}
@Override
@@ -233,7 +279,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
if (decor != null && DesktopModeStatus.isEnabled()
&& decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo));
+ mDesktopTasksController.moveToSplit(decor.mTaskInfo);
}
}
}
@@ -300,8 +346,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Override
public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
- final DesktopModeWindowDecoration decoration =
- mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (decoration == null) return;
decoration.close();
@@ -309,18 +354,29 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (mEventReceiversByDisplay.contains(displayId)) {
removeTaskFromEventReceiver(displayId);
}
+ // Remove the decoration from the cache last because WindowDecoration#close could still
+ // issue CANCEL MotionEvents to touch listeners before its view host is released.
+ // See b/327664694.
+ mWindowDecorByTaskId.remove(taskInfo.taskId);
}
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
View.OnGenericMotionListener , DragDetector.MotionEventHandler {
private static final int CLOSE_MAXIMIZE_MENU_DELAY_MS = 150;
+
private final int mTaskId;
private final WindowContainerToken mTaskToken;
private final DragPositioningCallback mDragPositioningCallback;
private final DragDetector mDragDetector;
private final GestureDetector mGestureDetector;
+ /**
+ * Whether to pilfer the next motion event to send cancellations to the windows below.
+ * Useful when the caption window is spy and the gesture should be handle by the system
+ * instead of by the app for their custom header content.
+ */
+ private boolean mShouldPilferCaptionEvents;
private boolean mIsDragging;
private boolean mTouchscreenInUse;
private boolean mHasLongClicked;
@@ -367,14 +423,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
decoration.closeHandleMenu();
}
} else if (id == R.id.desktop_button) {
- if (mDesktopTasksController.isPresent()) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // App sometimes draws before the insets from WindowDecoration#relayout have
- // been added, so they must be added here
- mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
- mDesktopTasksController.get().moveToDesktop(mTaskId, wct);
- closeOtherSplitTask(mTaskId);
- }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // App sometimes draws before the insets from WindowDecoration#relayout have
+ // been added, so they must be added here
+ mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
+ mDesktopTasksController.moveToDesktop(mTaskId, wct);
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
decoration.closeHandleMenu();
@@ -382,42 +435,37 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mSplitScreenController.moveTaskToFullscreen(mTaskId,
SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
- mDesktopTasksController.ifPresent(c ->
- c.moveToFullscreen(mTaskId));
+ mDesktopTasksController.moveToFullscreen(mTaskId);
}
} else if (id == R.id.split_screen_button) {
decoration.closeHandleMenu();
- mDesktopTasksController.ifPresent(c -> {
- c.requestSplit(decoration.mTaskInfo);
- });
+ mDesktopTasksController.requestSplit(decoration.mTaskInfo);
} else if (id == R.id.collapse_menu_button) {
decoration.closeHandleMenu();
} else if (id == R.id.select_button) {
if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) {
// TODO(b/278084491): dev option to enable display switching
// remove when select is implemented
- mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
+ mDesktopTasksController.moveToNextDisplay(mTaskId);
}
} else if (id == R.id.maximize_window) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
- mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
+ mDesktopTasksController.toggleDesktopTaskSize(taskInfo);
} else if (id == R.id.maximize_menu_maximize_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
+ mDesktopTasksController.toggleDesktopTaskSize(taskInfo);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
} else if (id == R.id.maximize_menu_snap_left_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
- taskInfo, SnapPosition.LEFT));
+ mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.LEFT);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
} else if (id == R.id.maximize_menu_snap_right_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
- taskInfo, SnapPosition.RIGHT));
+ mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.RIGHT);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
}
@@ -438,6 +486,40 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
moveTaskToFront(decoration.mTaskInfo);
+ final int actionMasked = e.getActionMasked();
+ final boolean isDown = actionMasked == MotionEvent.ACTION_DOWN;
+ final boolean isUpOrCancel = actionMasked == MotionEvent.ACTION_CANCEL
+ || actionMasked == MotionEvent.ACTION_UP;
+ if (isDown) {
+ final boolean downInCustomizableCaptionRegion =
+ decoration.checkTouchEventInCustomizableRegion(e);
+ final boolean downInExclusionRegion = mExclusionRegion.contains(
+ (int) e.getRawX(), (int) e.getRawY());
+ final boolean isTransparentCaption =
+ TaskInfoKt.isTransparentCaptionBarAppearance(decoration.mTaskInfo);
+ // The caption window may be a spy window when the caption background is
+ // transparent, which means events will fall through to the app window. Make
+ // sure to cancel these events if they do not happen in the intersection of the
+ // customizable region and what the app reported as exclusion areas, because
+ // the drag-move or other caption gestures should take priority outside those
+ // regions.
+ mShouldPilferCaptionEvents = !(downInCustomizableCaptionRegion
+ && downInExclusionRegion && isTransparentCaption);
+ }
+
+ if (!mShouldPilferCaptionEvents) {
+ // The event will be handled by a window below.
+ return false;
+ }
+ // Otherwise pilfer so that windows below receive cancellations for this gesture, and
+ // continue normal handling as a caption gesture.
+ if (mInputManager != null) {
+ mInputManager.pilferPointers(v.getViewRootImpl().getInputToken());
+ }
+ if (isUpOrCancel) {
+ // Gesture is finished, reset state.
+ mShouldPilferCaptionEvents = false;
+ }
if (!mHasLongClicked && id != R.id.maximize_window) {
decoration.closeMaximizeMenuIfNeeded(e);
}
@@ -478,8 +560,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} else if (ev.getAction() == ACTION_HOVER_EXIT) {
if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) {
decoration.onMaximizeWindowHoverExit();
- } else if (id == R.id.maximize_window
- || MaximizeMenu.Companion.isMaximizeMenuView(id)) {
+ } else if (id == R.id.maximize_window || id == R.id.maximize_menu) {
// Close menu if not hovering over maximize menu or maximize button after a
// delay to give user a chance to re-enter view or to move from one maximize
// menu view to another.
@@ -493,7 +574,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!taskInfo.isFocused) {
- mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo));
+ mDesktopTasksController.moveTaskToFront(taskInfo);
}
}
@@ -537,10 +618,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
- mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo,
+ mDesktopTasksController.onDragPositioningMove(taskInfo,
decoration.mTaskSurface,
e.getRawX(dragPointerIdx),
- newTaskBounds));
+ newTaskBounds);
mIsDragging = true;
return true;
}
@@ -562,10 +643,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
(int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx)));
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
- mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
- position,
+ mDesktopTasksController.onDragPositioningEnd(taskInfo, position,
new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
- newTaskBounds));
+ newTaskBounds);
if (touchingButton && !mHasLongClicked) {
// We need the input event to not be consumed here to end the ripple
// effect on the touched button. We will reset drag state in the ensuing
@@ -593,10 +673,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
&& action != MotionEvent.ACTION_CANCEL)) {
return false;
}
- mDesktopTasksController.ifPresent(c -> {
- final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
- c.toggleDesktopTaskSize(decoration.mTaskInfo);
- });
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo);
return true;
}
}
@@ -760,20 +838,29 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return;
}
if (mTransitionDragActive) {
+ final DesktopModeVisualIndicator.IndicatorType indicatorType =
+ mDesktopTasksController.updateVisualIndicator(relevantDecor.mTaskInfo,
+ relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY());
mTransitionDragActive = false;
- final int statusBarHeight = getStatusBarHeight(
- relevantDecor.mTaskInfo.displayId);
- if (ev.getRawY() > 2 * statusBarHeight) {
+ if (indicatorType == TO_DESKTOP_INDICATOR
+ || indicatorType == TO_SPLIT_LEFT_INDICATOR
+ || indicatorType == TO_SPLIT_RIGHT_INDICATOR) {
if (DesktopModeStatus.isEnabled()) {
animateToDesktop(relevantDecor, ev);
}
mMoveToDesktopAnimator = null;
return;
} else if (mMoveToDesktopAnimator != null) {
- mDesktopTasksController.ifPresent(
- c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo));
+ mDesktopTasksController.onDragPositioningEndThroughStatusBar(
+ new PointF(ev.getRawX(), ev.getRawY()),
+ relevantDecor.mTaskInfo,
+ calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE));
mMoveToDesktopAnimator = null;
return;
+ } else {
+ // In cases where we create an indicator but do not start the
+ // move-to-desktop animation, we need to dismiss it.
+ mDesktopTasksController.releaseVisualIndicator();
}
}
relevantDecor.checkClickEvent(ev);
@@ -785,20 +872,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return;
}
if (mTransitionDragActive) {
- mDesktopTasksController.ifPresent(
- c -> c.updateVisualIndicator(
+ final DesktopModeVisualIndicator.IndicatorType indicatorType =
+ mDesktopTasksController.updateVisualIndicator(
relevantDecor.mTaskInfo,
- relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()));
- final int statusBarHeight = getStatusBarHeight(
- relevantDecor.mTaskInfo.displayId);
- if (ev.getRawY() > statusBarHeight) {
+ relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY());
+ if (indicatorType != TO_FULLSCREEN_INDICATOR) {
if (mMoveToDesktopAnimator == null) {
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
mContext, mDragToDesktopAnimationStartBounds,
relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
- mDesktopTasksController.ifPresent(
- c -> c.startDragToDesktop(relevantDecor.mTaskInfo,
- mMoveToDesktopAnimator));
+ mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
+ mMoveToDesktopAnimator);
}
}
if (mMoveToDesktopAnimator != null) {
@@ -844,6 +928,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
* Animates a window to the center, grows to freeform size, and transitions to Desktop Mode.
* @param relevantDecor the window decor of the task to be animated
* @param ev the motion event that triggers the animation
+ * TODO(b/315527000): This animation needs to be adjusted to allow snap left/right cases.
+ * Currently fullscreen -> split snap still animates to center screen before readjusting.
*/
private void centerAndMoveToDesktopWithAnimation(DesktopModeWindowDecoration relevantDecor,
MotionEvent ev) {
@@ -867,13 +953,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mDesktopTasksController.ifPresent(
- c -> {
- c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo,
- calculateFreeformBounds(ev.getDisplayId(),
- DesktopTasksController
- .DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
- });
+ mDesktopTasksController.onDragPositioningEndThroughStatusBar(
+ new PointF(ev.getRawX(), ev.getRawY()),
+ relevantDecor.mTaskInfo,
+ calculateFreeformBounds(ev.getDisplayId(),
+ DesktopTasksController
+ .DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
}
});
animator.start();
@@ -1003,7 +1088,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final DragPositioningCallback dragPositioningCallback;
final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.desktop_mode_transition_area_height);
+ R.dimen.desktop_mode_fullscreen_from_desktop_height);
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
dragPositioningCallback = new FluidResizeTaskPositioner(
mTaskOrganizer, mTransitions, windowDecoration, mDisplayController,
@@ -1038,12 +1123,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return mSplitScreenController.getTaskInfo(remainingTaskPosition);
}
- private void closeOtherSplitTask(int taskId) {
- if (isTaskInSplitScreen(taskId)) {
- mTaskOperations.closeTask(getOtherSplitTask(taskId).token);
- }
- }
-
private boolean isTaskInSplitScreen(int taskId) {
return mSplitScreenController != null
&& mSplitScreenController.isTaskInSplitScreen(taskId);
@@ -1108,12 +1187,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Override
public void onExclusionRegionChanged(int taskId, Region region) {
- mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region));
+ mDesktopTasksController.onExclusionRegionChanged(taskId, region);
}
@Override
public void onExclusionRegionDismissed(int taskId) {
- mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId));
+ mDesktopTasksController.removeExclusionRegionForTask(taskId);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 74f460bf1226..39803e2afd34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -56,6 +56,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;
@@ -317,24 +318,23 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
- // The "app controls" type caption bar should report the occluding elements as bounding
- // rects to the insets system so that apps can draw in the empty space left in the center.
- if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor) {
- // The "app chip" section of the caption bar, it's aligned to the left and its width
- // varies depending on the length of the app name, but we'll report its max width for
- // now.
- // TODO(b/316387515): consider reporting the true width after it's been laid out.
+ if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor
+ && TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
+ // App is requesting to customize the caption bar. Allow input to fall through to the
+ // windows below so that the app can respond to input events on their custom content.
+ relayoutParams.mAllowCaptionInputFallthrough = true;
+ // Report occluding elements as bounding rects to the insets system so that apps can
+ // draw in the empty space in the center:
+ // First, the "app chip" section of the caption bar (+ some extra margins).
final RelayoutParams.OccludingCaptionElement appChipElement =
new RelayoutParams.OccludingCaptionElement();
- appChipElement.mWidthResId = R.dimen.desktop_mode_app_details_max_width;
+ appChipElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_start;
appChipElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.START;
relayoutParams.mOccludingCaptionElements.add(appChipElement);
- // The "controls" section of the caption bar (maximize, close btns). These are aligned
- // to the right of the caption bar and have a fixed width.
- // TODO(b/316387515): add additional padding for an exclusive drag-move region.
+ // Then, the right-aligned section (drag space, maximize and close buttons).
final RelayoutParams.OccludingCaptionElement controlsElement =
new RelayoutParams.OccludingCaptionElement();
- controlsElement.mWidthResId = R.dimen.desktop_mode_right_edge_buttons_width;
+ controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end;
controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END;
relayoutParams.mOccludingCaptionElements.add(controlsElement);
}
@@ -699,6 +699,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
+ * Checks whether the touch event falls inside the customizable caption region.
+ */
+ boolean checkTouchEventInCustomizableRegion(MotionEvent ev) {
+ return mResult.mCustomizableCaptionRegion.contains((int) ev.getRawX(), (int) ev.getRawY());
+ }
+
+ /**
* Check a passed MotionEvent if a click has occurred on any button on this caption
* Note this should only be called when a regular onClick is not possible
* (i.e. the button was clicked through status bar layer)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index dc65855646ea..32c2d1e9b257 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -31,6 +31,7 @@ import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Binder;
import android.view.Display;
import android.view.InsetsSource;
@@ -311,6 +312,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
if (numOfElements == 0) {
boundingRects = null;
} else {
+ // The customizable region can at most be equal to the caption bar.
+ if (params.mAllowCaptionInputFallthrough) {
+ outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
+ }
boundingRects = new Rect[numOfElements];
for (int i = 0; i < numOfElements; i++) {
final OccludingCaptionElement element =
@@ -319,9 +324,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
resources.getDimensionPixelSize(element.mWidthResId);
boundingRects[i] =
calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
+ // Subtract the regions used by the caption elements, the rest is
+ // customizable.
+ if (params.mAllowCaptionInputFallthrough) {
+ outResult.mCustomizableCaptionRegion.op(boundingRects[i],
+ Region.Op.DIFFERENCE);
+ }
}
}
-
// Add this caption as an inset source.
wct.addInsetsSource(mTaskInfo.token,
mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
@@ -389,6 +399,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
lp.setTrustedOverlay();
+ if (params.mAllowCaptionInputFallthrough) {
+ lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ } else {
+ lp.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ }
if (mViewHost == null) {
mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
mCaptionWindowManager);
@@ -596,6 +611,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
int mCaptionHeightId;
int mCaptionWidthId;
final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>();
+ boolean mAllowCaptionInputFallthrough;
int mShadowRadiusId;
int mCornerRadius;
@@ -610,6 +626,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mCaptionHeightId = Resources.ID_NULL;
mCaptionWidthId = Resources.ID_NULL;
mOccludingCaptionElements.clear();
+ mAllowCaptionInputFallthrough = false;
mShadowRadiusId = Resources.ID_NULL;
mCornerRadius = 0;
@@ -637,6 +654,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
int mCaptionHeight;
int mCaptionWidth;
int mCaptionX;
+ final Region mCustomizableCaptionRegion = Region.obtain();
int mWidth;
int mHeight;
T mRootView;
@@ -647,6 +665,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mCaptionHeight = 0;
mCaptionWidth = 0;
mCaptionX = 0;
+ mCustomizableCaptionRegion.setEmpty();
mRootView = null;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
new file mode 100644
index 000000000000..5dd96aceaec7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor.extension
+
+import android.app.TaskInfo
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS
+import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
+
+val TaskInfo.isTransparentCaptionBarAppearance: Boolean
+ get() {
+ val appearance = taskDescription?.statusBarAppearance ?: 0
+ return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0
+ }
+
+val TaskInfo.isLightCaptionBarAppearance: Boolean
+ get() {
+ val appearance = taskDescription?.statusBarAppearance ?: 0
+ return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0
+ }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 7e5b9bd649f2..58bbb030da01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -8,12 +8,11 @@ import android.graphics.Bitmap
import android.graphics.Color
import android.view.View
import android.view.View.OnLongClickListener
-import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS
-import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.withStyledAttributes
+import androidx.core.view.isVisible
import com.android.internal.R.attr.materialColorOnSecondaryContainer
import com.android.internal.R.attr.materialColorOnSurface
import com.android.internal.R.attr.materialColorSecondaryContainer
@@ -22,6 +21,8 @@ import com.android.internal.R.attr.materialColorSurfaceContainerLow
import com.android.internal.R.attr.materialColorSurfaceDim
import com.android.wm.shell.R
import com.android.wm.shell.windowdecor.MaximizeButtonView
+import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance
+import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance
/**
* A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
@@ -76,6 +77,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
closeWindowButton.imageTintList = ColorStateList.valueOf(color)
maximizeWindowButton.imageTintList = ColorStateList.valueOf(color)
expandMenuButton.imageTintList = ColorStateList.valueOf(color)
+ appNameTextView.isVisible = !taskInfo.isTransparentCaptionBarAppearance
appNameTextView.setTextColor(color)
appIconImageView.imageAlpha = alpha
maximizeWindowButton.imageAlpha = alpha
@@ -107,7 +109,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
@ColorInt
private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int {
- if (isTransparentBackgroundRequested(taskInfo)) {
+ if (taskInfo.isTransparentCaptionBarAppearance) {
return Color.TRANSPARENT
}
val materialColorAttr: Int =
@@ -133,10 +135,10 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
@ColorInt
private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo): Int {
val materialColorAttr = when {
- isTransparentBackgroundRequested(taskInfo) &&
- isLightCaptionBar(taskInfo) -> materialColorOnSecondaryContainer
- isTransparentBackgroundRequested(taskInfo) &&
- !isLightCaptionBar(taskInfo) -> materialColorOnSurface
+ taskInfo.isTransparentCaptionBarAppearance &&
+ taskInfo.isLightCaptionBarAppearance -> materialColorOnSecondaryContainer
+ taskInfo.isTransparentCaptionBarAppearance &&
+ !taskInfo.isLightCaptionBarAppearance -> materialColorOnSurface
isDarkMode() -> materialColorOnSurface
else -> materialColorOnSecondaryContainer
}
@@ -167,16 +169,6 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
Configuration.UI_MODE_NIGHT_YES
}
- private fun isTransparentBackgroundRequested(taskInfo: RunningTaskInfo): Boolean {
- val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0
- return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0
- }
-
- private fun isLightCaptionBar(taskInfo: RunningTaskInfo): Boolean {
- val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0
- return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0
- }
-
companion object {
private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"
private const val DARK_THEME_UNFOCUSED_OPACITY = 140 // 55%
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 01e2f988fbfc..2c0aa12f22d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -38,6 +38,7 @@ import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.view.inputmethod.ImeTracker;
import androidx.test.filters.SmallTest;
@@ -51,6 +52,12 @@ import org.mockito.MockitoAnnotations;
import java.util.concurrent.Executor;
+/**
+ * Tests for the display IME controller.
+ *
+ * <p> Build/Install/Run:
+ * atest WMShellUnitTests:DisplayImeControllerTest
+ */
@SmallTest
public class DisplayImeControllerTest extends ShellTestCase {
@@ -99,13 +106,13 @@ public class DisplayImeControllerTest extends ShellTestCase {
@Test
public void showInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.showInsets(ime(), true /* fromIme */, null /* statsToken */);
+ mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
verifyZeroInteractions(mExecutor);
}
@Test
public void hideInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.hideInsets(ime(), true /* fromIme */, null /* statsToken */);
+ mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
verifyZeroInteractions(mExecutor);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
index 956f1cd419c2..669e433ba386 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
@@ -50,6 +50,12 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
+/**
+ * Tests for the display insets controller.
+ *
+ * <p> Build/Install/Run:
+ * atest WMShellUnitTests:DisplayInsetsControllerTest
+ */
@SmallTest
public class DisplayInsetsControllerTest extends ShellTestCase {
@@ -114,9 +120,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
@@ -136,9 +142,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index f8ce4ee8e1ce..9703dce8bf53 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -56,18 +56,16 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
context, taskSurface, taskDisplayAreaOrganizer)
whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
+ whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS)
}
@Test
fun testFullscreenRegionCalculation() {
val transitionHeight = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_transition_area_height)
+ R.dimen.desktop_mode_fullscreen_from_desktop_height)
val fromFreeformWidth = mContext.resources.getDimensionPixelSize(
R.dimen.desktop_mode_fullscreen_from_desktop_width
)
- val fromFreeformHeight = mContext.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_fullscreen_from_desktop_height
- )
var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
@@ -77,7 +75,7 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
DISPLAY_BOUNDS.width() / 2 - fromFreeformWidth / 2,
-50,
DISPLAY_BOUNDS.width() / 2 + fromFreeformWidth / 2,
- fromFreeformHeight))
+ 2 * STABLE_INSETS.top))
testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT)
assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
@@ -135,5 +133,12 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
private const val TRANSITION_AREA_WIDTH = 32
private const val CAPTION_HEIGHT = 50
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
+ private const val NAVBAR_HEIGHT = 50
+ private val STABLE_INSETS = Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
+ )
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index d4e9ac96d221..e74c804d4f40 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -36,7 +36,6 @@ import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Rational;
@@ -45,6 +44,8 @@ import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;
+import androidx.test.filters.SmallTest;
+
import com.android.wm.shell.MockSurfaceControlHelper;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 5d968d3360ab..3384509f1da9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -42,10 +42,11 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.filters.SmallTest;
+
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
index 45f6c8c7f69f..72db6e091307 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
@@ -23,20 +23,21 @@ import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_EXPAND_COLLAPSE;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_FULLSCREEN;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE;
-import static java.util.Collections.EMPTY_LIST;
-
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static java.util.Collections.EMPTY_LIST;
+
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.graphics.drawable.Icon;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.filters.SmallTest;
+
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.pip.PipMediaController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 050443914355..fbc0db9c2850 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -32,7 +32,6 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.IBinder;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
@@ -40,6 +39,8 @@ import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.test.filters.SmallTest;
+
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.transition.Transitions;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index f84685a92b57..9bb5482de715 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -29,8 +29,10 @@ import android.hardware.display.VirtualDisplay
import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.util.SparseArray
import android.view.Choreographer
import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
import android.view.InputChannel
import android.view.InputMonitor
import android.view.InsetsSource
@@ -71,6 +73,8 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import java.util.Optional
import java.util.function.Supplier
+import org.mockito.Mockito
+import org.mockito.kotlin.spy
/** Tests of [DesktopModeWindowDecorViewModel] */
@@ -96,10 +100,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Mock private lateinit var mockShellExecutor: ShellExecutor
@Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock private lateinit var mockShellCommandHandler: ShellCommandHandler
+ @Mock private lateinit var mockWindowManager: IWindowManager
private val transactionFactory = Supplier<SurfaceControl.Transaction> {
SurfaceControl.Transaction()
}
+ private val windowDecorByTaskIdSpy = spy(SparseArray<DesktopModeWindowDecoration>())
private lateinit var shellInit: ShellInit
private lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener
@@ -108,12 +114,15 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Before
fun setUp() {
shellInit = ShellInit(mockShellExecutor)
+ windowDecorByTaskIdSpy.clear()
desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
mContext,
+ mockShellExecutor,
mockMainHandler,
mockMainChoreographer,
shellInit,
mockShellCommandHandler,
+ mockWindowManager,
mockTaskOrganizer,
mockDisplayController,
mockShellController,
@@ -124,7 +133,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
transactionFactory,
- mockRootTaskDisplayAreaOrganizer
+ mockRootTaskDisplayAreaOrganizer,
+ windowDecorByTaskIdSpy
)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -328,6 +338,19 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
verify(decoration, times(1)).relayout(task)
}
+ @Test
+ fun testDestroyWindowDecoration_closesBeforeCleanup() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val decoration = setUpMockDecorationForTask(task)
+ val inOrder = Mockito.inOrder(decoration, windowDecorByTaskIdSpy)
+
+ onTaskOpening(task)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+
+ inOrder.verify(decoration).close()
+ inOrder.verify(windowDecorByTaskIdSpy).remove(task.taskId)
+ }
+
private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
desktopModeWindowDecorViewModel.onTaskOpening(
task,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 40e61dd95f51..9e62bd254ac5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.windowdecor;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
@@ -168,6 +172,57 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
}
+ @Test
+ public void updateRelayoutParams_freeformAndTransparent_allowsInputFallthrough() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.taskDescription.setStatusBarAppearance(
+ APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(relayoutParams.mAllowCaptionInputFallthrough).isTrue();
+ }
+
+ @Test
+ public void updateRelayoutParams_freeformButOpaque_disallowsInputFallthrough() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.taskDescription.setStatusBarAppearance(0);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse();
+ }
+
+ @Test
+ public void updateRelayoutParams_fullscreen_disallowsInputFallthrough() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse();
+ }
+
private void fillRoundedCornersResources(int fillValue) {
when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt()))
.thenReturn(fillValue);
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index f0c639574a9f..49254d1c6f6e 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -81,7 +81,7 @@ ApkAssetsPtr ApkAssets::LoadOverlay(const std::string& idmap_path, package_prope
std::string overlay_path(loaded_idmap->OverlayApkPath());
auto fd = unique_fd(base::utf8::open(overlay_path.c_str(), O_RDONLY | O_CLOEXEC));
std::unique_ptr<AssetsProvider> overlay_assets;
- if (IsFabricatedOverlay(fd)) {
+ if (IsFabricatedOverlayName(overlay_path) && IsFabricatedOverlay(fd)) {
// Fabricated overlays do not contain resource definitions. All of the overlay resource values
// are defined inline in the idmap.
overlay_assets = EmptyAssetsProvider::Create(std::move(overlay_path));
@@ -137,8 +137,7 @@ ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
return {};
}
loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags);
- } else if (loaded_idmap != nullptr &&
- IsFabricatedOverlay(std::string(loaded_idmap->OverlayApkPath()))) {
+ } else if (loaded_idmap != nullptr && IsFabricatedOverlay(loaded_idmap->OverlayApkPath())) {
loaded_arsc = LoadedArsc::Load(loaded_idmap.get());
} else {
loaded_arsc = LoadedArsc::CreateEmpty();
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 5f98b8f8db43..982419059ead 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -18,8 +18,10 @@
#include "androidfw/Idmap.h"
+#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
#include "androidfw/misc.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
@@ -250,7 +252,12 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size
}
} // namespace
-LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header,
+// O_PATH is a lightweight way of creating an FD, only exists on Linux
+#ifndef O_PATH
+#define O_PATH (0)
+#endif
+
+LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
@@ -267,10 +274,10 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header,
configurations_(configs),
overlay_entries_(overlay_entries),
string_pool_(std::move(string_pool)),
- idmap_path_(std::move(idmap_path)),
+ idmap_fd_(android::base::utf8::open(idmap_path.c_str(), O_RDONLY|O_CLOEXEC|O_BINARY|O_PATH)),
overlay_apk_path_(overlay_apk_path),
target_apk_path_(target_apk_path),
- idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
+ idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) {}
std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
ATRACE_CALL();
@@ -368,7 +375,7 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie
}
bool LoadedIdmap::IsUpToDate() const {
- return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
+ return idmap_last_mod_time_ == getFileModDate(idmap_fd_.get());
}
} // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 2c99f1aa3675..a3dd9833219e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -54,6 +54,8 @@
#define INT32_MAX ((int32_t)(2147483647))
#endif
+using namespace std::literals;
+
namespace android {
#if defined(_WIN32)
@@ -237,12 +239,24 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs
fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
}
-bool IsFabricatedOverlay(const std::string& path) {
- return IsFabricatedOverlay(path.c_str());
+bool IsFabricatedOverlayName(std::string_view path) {
+ static constexpr auto suffixFrro = ".frro"sv;
+ static constexpr auto suffixIdmap = ".frro@idmap"sv;
+
+ return (path.size() > suffixFrro.size() && path.ends_with(suffixFrro))
+ || (path.size() > suffixIdmap.size() && path.ends_with(suffixIdmap));
}
-bool IsFabricatedOverlay(const char* path) {
- auto fd = base::unique_fd(base::utf8::open(path, O_RDONLY|O_CLOEXEC));
+bool IsFabricatedOverlay(std::string_view path) {
+ if (!IsFabricatedOverlayName(path)) {
+ return false;
+ }
+ std::string path_copy;
+ if (path[path.size()] != '\0') {
+ path_copy.assign(path);
+ path = path_copy;
+ }
+ auto fd = base::unique_fd(base::utf8::open(path.data(), O_RDONLY|O_CLOEXEC|O_BINARY));
if (fd < 0) {
return false;
}
@@ -7319,9 +7333,6 @@ class IdmapTypeMapping {
public:
void add(uint32_t targetResId, uint32_t overlayResId) {
uint8_t targetTypeId = Res_GETTYPE(targetResId);
- if (mData.find(targetTypeId) == mData.end()) {
- mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>());
- }
auto& entries = mData[targetTypeId];
entries.insert(std::make_pair(targetResId, overlayResId));
}
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index d9f7c2a1ac19..c32a38ee9ec2 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -23,6 +23,7 @@
#include <variant>
#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/StringPiece.h"
#include "androidfw/ResourceTypes.h"
@@ -159,11 +160,6 @@ class LoadedIdmap {
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
static std::unique_ptr<LoadedIdmap> Load(StringPiece idmap_path, StringPiece idmap_data);
- // Returns the path to the IDMAP.
- std::string_view IdmapPath() const {
- return idmap_path_;
- }
-
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
std::string_view OverlayApkPath() const {
return overlay_apk_path_;
@@ -203,7 +199,7 @@ class LoadedIdmap {
const Idmap_overlay_entry* overlay_entries_;
const std::unique_ptr<ResStringPool> string_pool_;
- std::string idmap_path_;
+ android::base::unique_fd idmap_fd_;
std::string_view overlay_apk_path_;
std::string_view target_apk_path_;
time_t idmap_last_mod_time_;
@@ -211,7 +207,7 @@ class LoadedIdmap {
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
- explicit LoadedIdmap(std::string&& idmap_path,
+ explicit LoadedIdmap(const std::string& idmap_path,
const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 3d1403d0e039..c2648909386c 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -59,8 +59,8 @@ constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endi
constexpr const uint32_t kFabricatedOverlayCurrentVersion = 3;
// Returns whether or not the path represents a fabricated overlay.
-bool IsFabricatedOverlay(const std::string& path);
-bool IsFabricatedOverlay(const char* path);
+bool IsFabricatedOverlayName(std::string_view path);
+bool IsFabricatedOverlay(std::string_view path);
bool IsFabricatedOverlay(android::base::borrowed_fd fd);
/**
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index d40d24ede769..077609d20d55 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -43,6 +43,8 @@ typedef enum FileType {
FileType getFileType(const char* fileName);
/* get the file's modification date; returns -1 w/errno set on failure */
time_t getFileModDate(const char* fileName);
+/* same, but also returns -1 if the file has already been deleted */
+time_t getFileModDate(int fd);
// Check if |path| or |fd| resides on a readonly filesystem.
bool isReadonlyFilesystem(const char* path);
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index d3949e9cf69f..93dcaf549a90 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -76,13 +76,23 @@ FileType getFileType(const char* fileName)
/*
* Get a file's modification date.
*/
-time_t getFileModDate(const char* fileName)
-{
+time_t getFileModDate(const char* fileName) {
struct stat sb;
+ if (stat(fileName, &sb) < 0) {
+ return (time_t)-1;
+ }
+ return sb.st_mtime;
+}
- if (stat(fileName, &sb) < 0)
- return (time_t) -1;
-
+time_t getFileModDate(int fd) {
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ return (time_t)-1;
+ }
+ if (sb.st_nlink <= 0) {
+ errno = ENOENT;
+ return (time_t)-1;
+ }
return sb.st_mtime;
}
diff --git a/libs/hostgraphics/ADisplay.cpp b/libs/hostgraphics/ADisplay.cpp
new file mode 100644
index 000000000000..9cc1f40184e3
--- /dev/null
+++ b/libs/hostgraphics/ADisplay.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#include <apex/display.h>
+#include <utils/Errors.h>
+
+namespace android::display::impl {
+
+/**
+ * Implementation of ADisplayConfig
+ */
+struct DisplayConfigImpl {
+ /**
+ * The width in pixels of the display configuration.
+ */
+ int32_t width{1080};
+
+ /**
+ * The height in pixels of the display configuration.
+ */
+
+ int32_t height{1920};
+
+ /**
+ * The refresh rate of the display configuration, in frames per second.
+ */
+ float fps{60.0};
+
+ /**
+ * The vsync offset at which surfaceflinger runs, in nanoseconds.
+ */
+ int64_t sfOffset{0};
+
+ /**
+ * The vsync offset at which applications run, in nanoseconds.
+ */
+ int64_t appOffset{0};
+};
+
+// DisplayConfigImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value);
+
+/**
+ * Implementation of ADisplay
+ */
+struct DisplayImpl {
+ /**
+ * The type of the display, i.e. whether it is an internal or external
+ * display.
+ */
+ ADisplayType type;
+
+ /**
+ * The preferred WCG dataspace
+ */
+ ADataSpace wcgDataspace;
+
+ /**
+ * The preferred WCG pixel format
+ */
+ AHardwareBuffer_Format wcgPixelFormat;
+
+ /**
+ * The config for this display.
+ */
+ DisplayConfigImpl config;
+};
+
+// DisplayImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayImpl>::value);
+
+} // namespace android::display::impl
+
+using namespace android;
+using namespace android::display::impl;
+
+namespace android {
+
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
+ // This is running on host, so there are no physical displays available.
+ // Create 1 fake display instead.
+ DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
+ malloc(sizeof(DisplayImpl*) + sizeof(DisplayImpl)));
+ DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + 1);
+
+ displayData[0] = DisplayImpl{ADisplayType::DISPLAY_TYPE_INTERNAL,
+ ADataSpace::ADATASPACE_UNKNOWN,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ DisplayConfigImpl()};
+ impls[0] = displayData;
+ *outDisplays = reinterpret_cast<ADisplay**>(impls);
+ return 1;
+}
+
+void ADisplay_release(ADisplay** displays) {
+ if (displays == nullptr) {
+ return;
+ }
+ free(displays);
+}
+
+float ADisplay_getMaxSupportedFps(ADisplay* display) {
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ return impl->config.fps;
+}
+
+ADisplayType ADisplay_getDisplayType(ADisplay* display) {
+ return reinterpret_cast<DisplayImpl*>(display)->type;
+}
+
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+ AHardwareBuffer_Format* outPixelFormat) {
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ *outDataspace = impl->wcgDataspace;
+ *outPixelFormat = impl->wcgPixelFormat;
+}
+
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ *outConfig = reinterpret_cast<ADisplayConfig*>(&impl->config);
+ return OK;
+}
+
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
+ return reinterpret_cast<DisplayConfigImpl*>(config)->width;
+}
+
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config) {
+ return reinterpret_cast<DisplayConfigImpl*>(config)->height;
+}
+
+float ADisplayConfig_getFps(ADisplayConfig* config) {
+ return reinterpret_cast<DisplayConfigImpl*>(config)->fps;
+}
+
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) {
+ return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset;
+}
+
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) {
+ return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
+}
+
+} // namespace android
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index f166fdeafa41..4407af68de99 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -22,6 +22,7 @@ cc_library_host_static {
srcs: [
":libui_host_common",
+ "ADisplay.cpp",
"Fence.cpp",
"HostBufferQueue.cpp",
"PublicFormat.cpp",
@@ -32,16 +33,21 @@ cc_library_host_static {
// When frameworks/native/include will be removed from the list of automatic includes.
// We will have to copy necessary headers with a pre-build step (generated headers).
".",
- "frameworks/native/libs/nativebase/include",
- "frameworks/native/libs/nativewindow/include",
"frameworks/native/libs/arect/include",
"frameworks/native/libs/ui/include_private",
],
+
+ header_libs: [
+ "libnativebase_headers",
+ "libnativedisplay_headers",
+ "libnativewindow_headers",
+ ],
+
export_include_dirs: ["."],
target: {
windows: {
enabled: true,
- }
+ },
},
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 40239b8c2719..54f94f5c4b14 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -346,6 +346,7 @@ cc_defaults {
"jni/android_util_PathParser.cpp",
"jni/Bitmap.cpp",
+ "jni/BitmapRegionDecoder.cpp",
"jni/BufferUtils.cpp",
"jni/HardwareBufferHelpers.cpp",
"jni/BitmapFactory.cpp",
@@ -421,7 +422,6 @@ cc_defaults {
"jni/android_graphics_TextureLayer.cpp",
"jni/android_graphics_HardwareRenderer.cpp",
"jni/android_graphics_HardwareBufferRenderer.cpp",
- "jni/BitmapRegionDecoder.cpp",
"jni/GIFMovie.cpp",
"jni/GraphicsStatsService.cpp",
"jni/Movie.cpp",
@@ -559,8 +559,13 @@ cc_defaults {
"AnimatorManager.cpp",
"CanvasTransform.cpp",
"DamageAccumulator.cpp",
+ "DeviceInfo.cpp",
+ "FrameInfo.cpp",
+ "FrameInfoVisualizer.cpp",
+ "FrameMetricsReporter.cpp",
"Gainmap.cpp",
"Interpolator.cpp",
+ "JankTracker.cpp",
"LightingInfo.cpp",
"Matrix.cpp",
"Mesh.cpp",
@@ -623,13 +628,8 @@ cc_defaults {
"utils/NdkUtils.cpp",
"AutoBackendTextureRelease.cpp",
"DeferredLayerUpdater.cpp",
- "DeviceInfo.cpp",
- "FrameInfo.cpp",
- "FrameInfoVisualizer.cpp",
"HardwareBitmapUploader.cpp",
"HWUIProperties.sysprop",
- "JankTracker.cpp",
- "FrameMetricsReporter.cpp",
"Layer.cpp",
"LayerUpdateQueue.cpp",
"ProfileDataContainer.cpp",
@@ -643,7 +643,10 @@ cc_defaults {
cflags: ["-Wno-implicit-fallthrough"],
},
host: {
- header_libs: ["libnativebase_headers"],
+ header_libs: [
+ "libnativebase_headers",
+ "libnativedisplay_headers",
+ ],
local_include_dirs: ["platform/host"],
@@ -655,7 +658,11 @@ cc_defaults {
"platform/host/WebViewFunctorManager.cpp",
],
- cflags: ["-Wno-unused-private-field"],
+ cflags: [
+ "-DHWUI_NULL_GPU",
+ "-DNULL_GPU_MAX_TEXTURE_SIZE=4096",
+ "-Wno-unused-private-field",
+ ],
},
},
}
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 59f21694fb77..1e53fc2164c7 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -249,6 +249,7 @@ bool FrameInfoVisualizer::consumeProperties() {
}
void FrameInfoVisualizer::dumpData(int fd) {
+#ifdef __ANDROID__
RETURN_IF_PROFILING_DISABLED();
// This method logs the last N frames (where N is <= mDataSize) since the
@@ -268,6 +269,7 @@ void FrameInfoVisualizer::dumpData(int fd) {
durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
}
+#endif
}
} /* namespace uirenderer */
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 4b0ddd2fa2ef..638a060bdb1c 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -17,10 +17,10 @@
#include "JankTracker.h"
#include <cutils/ashmem.h>
+#include <cutils/trace.h>
#include <errno.h>
#include <inttypes.h>
#include <log/log.h>
-#include <sys/mman.h>
#include <algorithm>
#include <cmath>
@@ -278,7 +278,7 @@ void JankTracker::recomputeThresholds(int64_t frameBudget) REQUIRES(mDataMutex)
void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
const ProfileData* data) {
-
+#ifdef __ANDROID__
if (description) {
switch (description->type) {
case JankTrackerType::Generic:
@@ -296,9 +296,11 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
}
data->dump(fd);
dprintf(fd, "\n");
+#endif
}
void JankTracker::dumpFrames(int fd) {
+#ifdef __ANDROID__
dprintf(fd, "\n\n---PROFILEDATA---\n");
for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
dprintf(fd, "%s", FrameInfoNames[i]);
@@ -315,6 +317,7 @@ void JankTracker::dumpFrames(int fd) {
}
}
dprintf(fd, "\n---PROFILEDATA---\n\n");
+#endif
}
void JankTracker::reset() REQUIRES(mDataMutex) {
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index e358b57f6fe1..b1ad8b2eb1b9 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -16,32 +16,29 @@
#pragma once
-#ifdef __ANDROID__ // Layoutlib does not support device info
-#include "DeviceInfo.h"
-#endif // __ANDROID__
-
-#include "Outline.h"
-#include "Rect.h"
-#include "RevealClip.h"
-#include "effects/StretchEffect.h"
-#include "utils/MathUtils.h"
-#include "utils/PaintUtils.h"
-
#include <SkBlendMode.h>
-#include <SkImageFilter.h>
#include <SkCamera.h>
#include <SkColor.h>
+#include <SkImageFilter.h>
#include <SkMatrix.h>
#include <SkRegion.h>
-
#include <androidfw/ResourceTypes.h>
#include <cutils/compiler.h>
#include <stddef.h>
#include <utils/Log.h>
+
#include <algorithm>
#include <ostream>
#include <vector>
+#include "DeviceInfo.h"
+#include "Outline.h"
+#include "Rect.h"
+#include "RevealClip.h"
+#include "effects/StretchEffect.h"
+#include "utils/MathUtils.h"
+#include "utils/PaintUtils.h"
+
class SkBitmap;
class SkColorFilter;
class SkPaint;
@@ -546,13 +543,9 @@ public:
}
bool fitsOnLayer() const {
-#ifdef __ANDROID__ // Layoutlib does not support device info
const DeviceInfo* deviceInfo = DeviceInfo::get();
return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
-#else
- return mPrimitiveFields.mWidth <= 4096 && mPrimitiveFields.mHeight <= 4096;
-#endif
}
bool promotedToLayer() const {
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 536ff781badc..af169f4bc4cd 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -16,6 +16,7 @@
#include "VectorDrawable.h"
+#include <gui/TraceUtils.h>
#include <math.h>
#include <string.h>
#include <utils/Log.h>
@@ -26,12 +27,7 @@
#include "SkSamplingOptions.h"
#include "SkScalar.h"
#include "hwui/Paint.h"
-
-#ifdef __ANDROID__
#include "renderthread/RenderThread.h"
-#endif
-
-#include <gui/TraceUtils.h>
#include "utils/Macros.h"
#include "utils/VectorDrawableUtils.h"
@@ -544,7 +540,7 @@ bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
}
bool Tree::canReuseBitmap(Bitmap* bitmap, int width, int height) {
- return bitmap && width <= bitmap->width() && height <= bitmap->height();
+ return bitmap && width == bitmap->width() && height == bitmap->height();
}
void Tree::onPropertyChanged(TreeProperties* prop) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 8f3f82e06cf8..6f7024ae76b4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7878,9 +7878,9 @@ public class AudioManager {
*/
@FlaggedApi(FLAG_SUPPORTED_DEVICE_TYPES_API)
public @NonNull Set<Integer>
- getSupportedDeviceTypes(int direction) {
+ getSupportedDeviceTypes(@AudioDeviceRole int direction) {
if (direction != GET_DEVICES_OUTPUTS && direction != GET_DEVICES_INPUTS) {
- throw new IllegalArgumentException("AudioManager.getSupportedDeviceTypes("
+ throw new IllegalArgumentException("AudioManager.getSupportedDeviceTypes(0x"
+ Integer.toHexString(direction) + ") - Invalid.");
}
diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java
deleted file mode 100644
index b7c372182464..000000000000
--- a/media/java/android/media/RingtoneSelection.java
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * 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.media;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.FlaggedApi;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.vibrator.Flags;
-import android.provider.MediaStore;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Immutable representation a desired ringtone, usually originating from a user preference.
- * Unlike sound-only Uris, a "silent" setting is an explicit selection value, rather than null.
- *
- * <p>This representation can be converted into (or from) a URI form for storing within a string
- * preference or when using the ringtone picker via {@link RingtoneManager#ACTION_RINGTONE_PICKER}.
- * It does not carry any actual media data - it only references the components that make
- * up the preference. Initial selections can be built using {@link RingtoneSelection.Builder}.
- *
- * <p>A RingtoneSelection is typically played by passing into a {@link Ringtone.Builder}, and
- * supplementing with contextual defaults from the application. Bad Uris are handled by the
- * {@link Ringtone} class - the RingtoneSelection doesn't validate the target of the Uri.
- *
- * <p>When a RingtoneSelection is created/loaded, the values of its properties are modified
- * to be internally consistent and reflect effective values - with the exception of not verifying
- * the actual URI content. For example, loading a selection Uri that sets a sound source to
- * {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class
- * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}.
- *
- * <h2>Storing preferences</h2>
- *
- * <p>A ringtone preference can have several states: either unset, set to a ringtone selection Uri,
- * or, from prior to the introduction of {@code RingtoneSelection}, set to a sound-only Uri or
- * explicitly set to null to indicate silent.
- *
- * @hide
- */
-@TestApi
-@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
-public final class RingtoneSelection {
-
- /**
- * The sound source was specified but its value was not recognized. This value is used
- * internally for not stripping unrecognised (possibly future) values during processing.
- * @hide
- */
- public static final int SOUND_SOURCE_UNKNOWN = -1;
-
- /**
- * The sound source is not explicitly specified, so it can follow default behavior for its
- * context.
- */
- public static final int SOUND_SOURCE_UNSPECIFIED = 0;
-
- /**
- * Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker.
- */
- public static final int SOUND_SOURCE_OFF = 1;
-
- /**
- * The sound Uri should be used as the source of sound.
- */
- public static final int SOUND_SOURCE_URI = 2;
-
- /**
- * The sound should explicitly use the system default.
- *
- * <p>This value isn't valid within the system default itself.
- */
- public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3;
-
- // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT.
-
- /**
- * Directive for how to make sound.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "SOUND_SOURCE_", value = {
- SOUND_SOURCE_UNKNOWN,
- SOUND_SOURCE_UNSPECIFIED,
- SOUND_SOURCE_OFF,
- SOUND_SOURCE_URI,
- SOUND_SOURCE_SYSTEM_DEFAULT,
- })
- public @interface SoundSource {}
-
- /**
- * The vibration source was specified but its value was not recognized.
- * This value is used internally for not stripping unrecognised (possibly
- * future) values during processing.
- * @hide
- */
- public static final int VIBRATION_SOURCE_UNKNOWN = -1;
-
- /**
- * Vibration source is not explicitly specified. If vibration is enabled, this will use the
- * first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL},
- * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- */
- public static final int VIBRATION_SOURCE_UNSPECIFIED = 0;
-
- /** Specifies that vibration is explicitly disabled for this ringtone. */
- public static final int VIBRATION_SOURCE_OFF = 1;
-
- /** The vibration Uri should be used as the source of vibration. */
- public static final int VIBRATION_SOURCE_URI = 2;
-
- /**
- * The vibration should explicitly use the system default.
- *
- * <p>This value isn't valid within the system default itself.
- */
- public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3;
-
- /**
- * Specifies that vibration should use the vibration provided by the application. This is
- * typically the application's own default for the use-case, provided via
- * {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration
- * effect saved on the notification channel.
- *
- * <p>If no vibration is specified by the application, this value behaves if the source was
- * {@link #VIBRATION_SOURCE_UNSPECIFIED}.
- *
- * <p>This value isn't valid within the system default.
- */
- public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4;
-
- /**
- * Specifies that vibration should use haptic audio channels from the
- * sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified
- * by {@link #VIBRATION_SOURCE_UNSPECIFIED}.
- */
- // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements.
- public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10;
-
- /**
- * Specifies that vibration should generate haptic audio channels from the
- * audio tracks of the sound Uri.
- *
- * If the sound Uri already has haptic channels, then behaves as though
- * {@link #VIBRATION_SOURCE_AUDIO_CHANNEL} was specified instead.
- */
- public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11;
-
- /**
- * Directive for how to vibrate.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "VIBRATION_SOURCE_", value = {
- VIBRATION_SOURCE_UNKNOWN,
- VIBRATION_SOURCE_UNSPECIFIED,
- VIBRATION_SOURCE_OFF,
- VIBRATION_SOURCE_URI,
- VIBRATION_SOURCE_APPLICATION_DEFAULT,
- VIBRATION_SOURCE_AUDIO_CHANNEL,
- VIBRATION_SOURCE_HAPTIC_GENERATOR,
- })
- public @interface VibrationSource {}
-
- /**
- * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri
- * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF},
- * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning
- * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}.
- *
- * <p>This behavior is particularly suited to loading values from older settings that may
- * contain a raw sound Uri or null for silent.
- *
- * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
- */
- public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1;
-
- /**
- * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration
- * Uri for the returned {@link RingtoneSelection}, with null meaning
- * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs
- * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- *
- * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
- */
- public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2;
-
- /**
- * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid
- * value. Null or an invalid values will revert to default behavior correspnoding to
- * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs
- * ({@link RingtoneManager#getDefaultUri}) will set both
- * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- *
- * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false,
- * which include {@code null}.
- */
- public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3;
-
- /**
- * How to treat values in {@link #fromUri}.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "FROM_URI_", value = {
- FROM_URI_RINGTONE_SELECTION_OR_SOUND,
- FROM_URI_RINGTONE_SELECTION_OR_VIBRATION,
- FROM_URI_RINGTONE_SELECTION_ONLY
- })
- public @interface FromUriBehavior {}
-
- private static final String BASE_RINGTONE_URI = "content://media/ringtone";
- /**
- * String representation of a RingtoneSelection Uri that says to use defaults (equivalent
- * to {@code new RingtoneSelection.Builder().build()}).
- */
- public static final String DEFAULT_SELECTION_URI_STRING = BASE_RINGTONE_URI;
-
- private static final String MEDIA_URI_RINGTONE_PATH = "/ringtone";
-
- /* Query param keys. */
- private static final String URI_PARAM_SOUND_URI = "su";
- private static final String URI_PARAM_SOUND_SOURCE = "ss";
- private static final String URI_PARAM_VIBRATION_URI = "vu";
- private static final String URI_PARAM_VIBRATION_SOURCE = "vs";
-
- /* Common param values */
- private static final String SOURCE_OFF_STRING = "off";
- private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys";
-
- /* Vibration source param values. */
- private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac";
- private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app";
- private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg";
-
- @Nullable
- private final Uri mSoundUri;
- @SoundSource
- private final int mSoundSource;
-
- @Nullable
- private final Uri mVibrationUri;
- @VibrationSource
- private final int mVibrationSource;
-
- private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource,
- @Nullable Uri vibrationUri, @VibrationSource int vibrationSource) {
- // Enforce guarantees on the source values: revert to unspecified if they depend on
- // something that's not set.
- //
- // The non-public "unknown" value can't appear in a getter result, it's just a reserved
- // "null" value and should be treated the same as an unrecognized value. This can be seen
- // in Uri parsing. For this and other unrecognized values, we either revert them to the URI
- // source, if a Uri was included, or the "unspecified" source otherwise. This can be
- // seen in action in the Uri parsing.
- //
- // The "unspecified" source is a public value meaning that there is no specific
- // behavior indicated, and the defaults and fallbacks should be applied. For example, an
- // vibration source value of "system default" means to explicitly use the system default
- // vibration. However, an "unspecified" vibration source will first see if audio coupled
- // or application-default vibrations are available.
- mSoundSource = switch (soundSource) {
- // Supported explicit values that don't have a Uri.
- case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT ->
- soundSource;
- // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
- // unspecified.
- default ->
- soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED;
- };
- mVibrationSource = switch (vibrationSource) {
- // Enforce vibration sources that require a sound Uri.
- case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR ->
- soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED;
- // Supported explicit values that don't rely on any Uri.
- case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED,
- VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT ->
- vibrationSource;
- // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
- // unspecified.
- default ->
- vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED;
- };
- // Clear Uri values if they're un-used by the source.
- mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null;
- mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null;
- }
-
- /**
- * Returns the stored sound behavior.
- */
- @SoundSource
- public int getSoundSource() {
- return mSoundSource;
- }
-
- /**
- * Returns the sound Uri for this selection. This is guaranteed to be non-null if
- * {@link #getSoundSource} returns {@link #SOUND_SOURCE_URI}.
- */
- @Nullable
- public Uri getSoundUri() {
- return mSoundUri;
- }
-
- /**
- * Returns the selected vibration behavior.
- */
- @VibrationSource
- public int getVibrationSource() {
- return mVibrationSource;
- }
-
- /**
- * Returns the vibration Uri for this selection. This is guaranteed to be non-null if
- * {@link #getVibrationSource} returns {@link #SOUND_SOURCE_URI}.
- */
- @Nullable
- public Uri getVibrationUri() {
- return mVibrationUri;
- }
-
- /**
- * Converts the ringtone selection into a Uri-form, suitable for storing as a user preference
- * or returning as a result.
- */
- @NonNull
- public Uri toUri() {
- Uri.Builder builder = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(MediaStore.AUTHORITY)
- .path(MEDIA_URI_RINGTONE_PATH);
- if (mSoundUri != null) {
- builder.appendQueryParameter(URI_PARAM_SOUND_URI, mSoundUri.toString());
- }
- // Only off is explicit for sound sources
- String soundSourceStr = soundSourceToString(mSoundSource);
- if (soundSourceStr != null) {
- builder.appendQueryParameter(URI_PARAM_SOUND_SOURCE, soundSourceStr);
- }
- if (mVibrationUri != null) {
- builder.appendQueryParameter(URI_PARAM_VIBRATION_URI, mVibrationUri.toString());
- }
- String vibrationSourceStr = vibrationSourceToString(mVibrationSource);
- if (vibrationSourceStr != null) {
- builder.appendQueryParameter(URI_PARAM_VIBRATION_SOURCE, vibrationSourceStr);
- }
- return builder.build();
- }
-
- /**
- * Returns true if the Uri is an encoded {@link RingtoneSelection}. This method doesn't
- * validate the parameters of the selection.
- *
- * @see #fromUri
- * @see #toUri
- */
- public static boolean isRingtoneSelectionUri(@Nullable Uri uri) {
- if (uri == null) {
- return false;
- }
- // Any URI content://media/ringtone
- return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
- && MediaStore.AUTHORITY.equals(
- ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()))
- && MEDIA_URI_RINGTONE_PATH.equals(uri.getPath());
- }
-
- /**
- * Strip the specified userId from internal Uris. Non-stripped userIds will typically be
- * for work profiles referencing system ringtones from the host user.
- *
- * This is only for use in RingtoneManager.
- * @hide
- */
- @VisibleForTesting
- public RingtoneSelection getWithoutUserId(int userIdToStrip) {
- if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) {
- return this;
- }
-
- // Ok if uri is null. We only replace explicit references to the specified (current) userId.
- int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL);
- int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL);
- boolean needToChangeSound =
- soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip;
- boolean needToChangeVibration =
- vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip;
-
- // Anything to do?
- if (!needToChangeSound && !needToChangeVibration) {
- return this;
- }
-
- RingtoneSelection.Builder updated = new Builder(this);
- // The relevant uris can't be null, because they contain userIdToStrip.
- if (needToChangeSound) {
- // mSoundUri is not null, so the result of getUriWithoutUserId won't be null.
- updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri));
- }
- if (needToChangeVibration) {
- updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri));
- }
- return updated.build();
- }
-
- @Override
- public String toString() {
- return toUri().toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof RingtoneSelection other)) {
- return false;
- }
- return this.mSoundSource == other.mSoundSource
- && this.mVibrationSource == other.mVibrationSource
- && Objects.equals(this.mSoundUri, other.mSoundUri)
- && Objects.equals(this.mVibrationUri, other.mVibrationUri);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri);
- }
-
- /**
- * Converts a Uri into a RingtoneSelection.
- *
- * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for
- * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated
- * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter.
- *
- * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1,
- * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and
- * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- *
- * @param uri The Uri to convert, potentially null.
- * @param unrecognizedValueBehavior indicates how to treat values for which
- * {@link #isRingtoneSelectionUri(Uri)} returns false (including null).
- * @return the RingtoneSelection represented by the given uri.
- */
- @NonNull
- public static RingtoneSelection fromUri(@Nullable Uri uri,
- @FromUriBehavior int unrecognizedValueBehavior) {
- if (isRingtoneSelectionUri(uri)) {
- return parseRingtoneSelectionUri(uri);
- }
- // Old symbolic default URIs map to the sources suggested by the unrecognized behavior.
- // It doesn't always map to both sources because the app may have its own default behavior
- // to apply, so non-primary sources are left as unspecified, which will revert to the
- // system default in the absence of an app default.
- boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0;
- RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
- switch (unrecognizedValueBehavior) {
- case FROM_URI_RINGTONE_SELECTION_ONLY:
- if (isDefaultUri) {
- builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
- builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT);
- }
- // Always return unspecified (defaults) for unrecognized ringtone selection Uris.
- return builder.build();
- case FROM_URI_RINGTONE_SELECTION_OR_SOUND:
- if (uri == null) {
- return builder.setSoundSource(SOUND_SOURCE_OFF).build();
- } else if (isDefaultUri) {
- return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build();
- } else {
- return builder.setSoundSource(uri).build();
- }
- case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
- if (uri == null) {
- return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build();
- } else if (isDefaultUri) {
- return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build();
- } else {
- // Unlike sound, there's no legacy settings alias uri for the default.
- return builder.setVibrationSource(uri).build();
- }
- default:
- throw new IllegalArgumentException("Unknown behavior parameter: "
- + unrecognizedValueBehavior);
- }
- }
-
- /**
- * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}.
- * As a special case to be more compatible, if the RingtoneSelection has a userId specified in
- * the authority, then this is pushed down into the sound and vibration Uri, as the selection
- * itself is agnostic.
- */
- @NonNull
- private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) {
- RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
- int soundSource = stringToSoundSource(uri.getQueryParameter(URI_PARAM_SOUND_SOURCE));
- int vibrationSource = stringToVibrationSource(
- uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE));
- Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI);
- Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI);
-
- // Add userId if necessary. This won't override an existing one in the sound/vib Uris.
- int userIdFromAuthority = ContentProvider.getUserIdFromAuthority(
- uri.getAuthority(), UserHandle.USER_NULL);
- if (userIdFromAuthority != UserHandle.USER_NULL) {
- // Won't override existing user id.
- if (soundUri != null) {
- soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority);
- }
- if (vibrationUri != null) {
- vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority);
- }
- }
-
- // Set sound uri if present, but map system default Uris to the system default source.
- if (soundUri != null) {
- if (RingtoneManager.getDefaultType(uri) >= 0) {
- builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
- } else {
- builder.setSoundSource(soundUri);
- }
- }
- if (vibrationUri != null) {
- builder.setVibrationSource(vibrationUri);
- }
-
- // Don't set the source if there's a URI and the source is default, because that will
- // override the Uri source set above. In effect, we prioritise "explicit" sources over
- // an implicit Uri source - except for "default", which isn't really explicit.
- if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) {
- builder.setSoundSource(soundSource);
- }
- if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) {
- builder.setVibrationSource(vibrationSource);
- }
- return builder.build();
- }
-
- @Nullable
- private static Uri getParamAsUri(@NonNull Uri uri, String param) {
- // This returns the uri-decoded value, no need to further decode.
- String value = uri.getQueryParameter(param);
- if (value == null) {
- return null;
- }
- return Uri.parse(value);
- }
-
- /**
- * Converts the {@link SoundSource} to the uri query param value for it, or null
- * if the sound source is default, unknown, or implicit (uri).
- */
- @Nullable
- private static String soundSourceToString(@SoundSource int soundSource) {
- return switch (soundSource) {
- case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING;
- case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
- default -> null;
- };
- }
-
- /**
- * Returns the sound source int corresponding to the query string value. Returns
- * {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and
- * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri).
- */
- @SoundSource
- private static int stringToSoundSource(@Nullable String soundSource) {
- if (soundSource == null) {
- return SOUND_SOURCE_UNSPECIFIED;
- }
- return switch (soundSource) {
- case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF;
- case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT;
- default -> SOUND_SOURCE_UNKNOWN;
- };
- }
-
- /**
- * Converts the {@code vibrationSource} to the uri query param value for it, or null
- * if the vibration source is default, unknown, or implicit (uri).
- */
- @Nullable
- private static String vibrationSourceToString(@VibrationSource int vibrationSource) {
- return switch (vibrationSource) {
- case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING;
- case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
- case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
- case VIBRATION_SOURCE_APPLICATION_DEFAULT ->
- VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING;
- case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
- default -> null;
- };
- }
-
- @VibrationSource
- private static int stringToVibrationSource(@Nullable String vibrationSource) {
- if (vibrationSource == null) {
- return VIBRATION_SOURCE_UNSPECIFIED;
- }
- return switch (vibrationSource) {
- case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF;
- case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT;
- case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL;
- case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR;
- case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING ->
- VIBRATION_SOURCE_APPLICATION_DEFAULT;
- default -> VIBRATION_SOURCE_UNKNOWN;
- };
- }
-
- /**
- * Builder for {@link RingtoneSelection}. In general, this builder will be used by interfaces
- * allowing the user to configure their selection. Once a selection is stored as a Uri, then
- * the RingtoneSelection can be loaded directly using {@link RingtoneSelection#fromUri}.
- */
- @FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
- public static final class Builder {
- private Uri mSoundUri;
- private Uri mVibrationUri;
- @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED;
- @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED;
-
- /**
- * Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its
- * sound and vibration source unspecified, which means they would fall back to app/system
- * defaults.
- */
- public Builder() {}
-
- /**
- * Creates a builder initialized with the given ringtone selection.
- */
- public Builder(@NonNull RingtoneSelection selection) {
- requireNonNull(selection);
- mSoundSource = selection.getSoundSource();
- mSoundUri = selection.getSoundUri();
- mVibrationSource = selection.getVibrationSource();
- mVibrationUri = selection.getVibrationUri();
- }
-
- /**
- * Sets the desired sound source.
- *
- * <p>Values other than {@link #SOUND_SOURCE_URI} will clear any previous sound Uri.
- * For {@link #SOUND_SOURCE_URI}, the {@link #setSoundSource(Uri)} method should be
- * used instead, as setting it here will have no effect unless the Uri is also set.
- */
- @NonNull
- public Builder setSoundSource(@SoundSource int soundSource) {
- mSoundSource = soundSource;
- if (soundSource != SOUND_SOURCE_URI && soundSource != SOUND_SOURCE_UNKNOWN) {
- // Note that this means the configuration of "silent sound, but use haptic
- // generator" is currently not supported. Future support could be added by either
- // using the vibration uri in that case, or by having a special
- // "setSoundUriForVibrationOnly(Uri)" method that sets sound source to off but
- // also retains the Uri.
- mSoundUri = null;
- }
- return this;
- }
-
- /**
- * Sets the sound source to {@link #SOUND_SOURCE_URI}, and the sound Uri to the
- * specified {@link Uri}.
- */
- @NonNull
- public Builder setSoundSource(@NonNull Uri soundUri) {
- // getCanonicalUri shouldn't return null. If it somehow did, then the
- // RingtoneSelection constructor will revert this to unspecified.
- mSoundUri = requireNonNull(soundUri).getCanonicalUri();
- mSoundSource = SOUND_SOURCE_URI;
- return this;
- }
-
- /**
- * Sets the vibration source to the specified value.
- *
- * <p>Values other than {@link #VIBRATION_SOURCE_URI} will clear any previous vibration Uri.
- * For {@link #VIBRATION_SOURCE_URI}, the {@link #setVibrationSource(Uri)} method should be
- * used instead, as setting it here will have no effect unless the Uri is also set.
- */
- @NonNull
- public Builder setVibrationSource(@VibrationSource int vibrationSource) {
- mVibrationSource = vibrationSource;
- if (vibrationSource != VIBRATION_SOURCE_URI
- && vibrationSource != VIBRATION_SOURCE_UNKNOWN) {
- mVibrationUri = null;
- }
- return this;
- }
-
- /**
- * Sets the vibration source to {@link #VIBRATION_SOURCE_URI}, and the vibration Uri to the
- * specified {@link Uri}.
- */
- @NonNull
- public Builder setVibrationSource(@NonNull Uri vibrationUri) {
- // getCanonicalUri shouldn't return null. If it somehow did, then the
- // RingtoneSelection constructor will revert this to unspecified.
- mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri();
- mVibrationSource = VIBRATION_SOURCE_URI;
- return this;
- }
-
- /**
- * Returns the ringtone Uri that was configured.
- */
- @NonNull
- public RingtoneSelection build() {
- return new RingtoneSelection(mSoundUri, mSoundSource, mVibrationUri, mVibrationSource);
- }
- }
-}
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index f8dd756c5f9c..6223acf8ffa4 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -24,7 +24,10 @@ import com.android.internal.util.AnnotationValidations;
import java.util.Objects;
/**
- * An instances of this class represents a session of media playback.
+ * An instance of this class represents a session of media playback used to report playback
+ * metrics and events.
+ *
+ * Create a new instance using {@link MediaMetricsManager#createPlaybackSession}.
*/
public final class PlaybackSession implements AutoCloseable {
private final @NonNull String mId;
@@ -80,6 +83,21 @@ public final class PlaybackSession implements AutoCloseable {
mManager.reportTrackChangeEvent(mId, event);
}
+ /**
+ * A session ID is used to identify a unique playback and to tie together lower-level
+ * playback components.
+ *
+ * Associate this session with a {@link MediaCodec} by passing the ID into
+ * {@link MediaFormat} through {@link MediaFormat#LOG_SESSION_ID} when
+ * creating the {@link MediaCodec}.
+ *
+ * Associate this session with an {@link AudioTrack} by calling
+ * {@link AudioTrack#setLogSessionId}.
+ *
+ * Associate this session with {@link MediaDrm} and {@link MediaCrypto} by calling
+ * {@link MediaDrm#getPlaybackComponent} and then calling
+ * {@link PlaybackComponent#setLogSessionId}.
+ */
public @NonNull LogSessionId getSessionId() {
return mLogSessionId;
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 8396005b1b63..0fc80dd55fa9 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2895,6 +2895,10 @@ static void extractBufferFromContext(
jint offset,
jint size,
std::shared_ptr<C2Buffer> *buffer) {
+ if ((offset + size) > context->capacity()) {
+ ALOGW("extractBufferFromContext: offset + size provided exceed capacity");
+ return;
+ }
*buffer = context->toC2Buffer(offset, size);
if (*buffer == nullptr) {
if (!context->mMemory) {
@@ -2995,18 +2999,15 @@ static void android_media_MediaCodec_native_queueLinearBlock(
"MediaCodec.LinearBlock#obtain method to obtain a compatible buffer.");
return;
}
- sp<CryptoInfosWrapper> cryptoInfos = new CryptoInfosWrapper{decltype(cryptoInfos->value)()};
- jint sampleSize = 0;
+ sp<CryptoInfosWrapper> cryptoInfos = nullptr;
+ jint sampleSize = totalSize;
if (cryptoInfoArray != nullptr) {
+ cryptoInfos = new CryptoInfosWrapper{decltype(cryptoInfos->value)()};
extractCryptoInfosFromObjectArray(env,
&sampleSize,
&cryptoInfos->value,
cryptoInfoArray,
&errorDetailMsg);
- } else {
- sampleSize = totalSize;
- std::unique_ptr<CodecCryptoInfo> cryptoInfo{new MediaCodecCryptoInfo(totalSize)};
- cryptoInfos->value.push_back(std::move(cryptoInfo));
}
if (env->ExceptionCheck()) {
// Creation of cryptoInfo failed. Let the exception bubble up.
diff --git a/media/lib/tvremote/tests/Android.bp b/media/lib/tvremote/tests/Android.bp
index f02cfc393c81..280c515e9a9e 100644
--- a/media/lib/tvremote/tests/Android.bp
+++ b/media/lib/tvremote/tests/Android.bp
@@ -17,6 +17,7 @@ android_test {
],
static_libs: [
"mockito-target-minus-junit4",
+ "androidx.test.rules",
],
platform_apis: true,
certificate: "platform",
diff --git a/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java b/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java
index e6e39390962e..3b38a4621389 100644
--- a/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java
+++ b/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java
@@ -29,7 +29,8 @@ import android.media.tv.ITvRemoteServiceInput;
import android.os.Binder;
import android.os.IBinder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import java.util.ArrayList;
diff --git a/media/mca/tests/Android.bp b/media/mca/tests/Android.bp
index f02b4c0a4bfb..04f083dee093 100644
--- a/media/mca/tests/Android.bp
+++ b/media/mca/tests/Android.bp
@@ -13,7 +13,10 @@ android_test {
"android.test.runner",
"android.test.base",
],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "androidx.test.rules",
+ ],
// Include all test java files.
srcs: ["src/**/*.java"],
platform_apis: true,
diff --git a/media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java b/media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java
index 474b00f77a34..44f9805010bb 100644
--- a/media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java
+++ b/media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java
@@ -16,18 +16,19 @@
package android.camera.mediaeffects.tests.functional;
-import android.media.filterfw.samples.CameraEffectsRecordingSample;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.KeyEvent;
-import android.util.Log;
-import android.content.Intent;
-import android.os.Environment;
import android.media.MediaMetadataRetriever;
+import android.media.filterfw.samples.CameraEffectsRecordingSample;
import android.net.Uri;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import androidx.test.filters.LargeTest;
+
import java.io.File;
public class EffectsVideoCapture extends ActivityInstrumentationTestCase2
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
index 9b643ad3c086..022f1da5ce5b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
@@ -16,9 +16,6 @@
package com.android.mediaframeworktest.functional;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
@@ -27,10 +24,14 @@ import android.hardware.Camera.ShutterCallback;
import android.os.ConditionVariable;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.view.SurfaceHolder;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+
import java.io.*;
/**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
index da106be78ac0..8f32e91ce5c5 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
@@ -18,9 +18,10 @@ package com.android.mediaframeworktest.functional;
import android.media.MediaMetadataRetriever;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
+import androidx.test.filters.MediumTest;
+
import com.android.mediaframeworktest.MediaNames;
import com.android.mediaframeworktest.MediaProfileReader;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java
index 728e68dd8dd6..83793ee22b0a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java
@@ -16,21 +16,20 @@
package com.android.mediaframeworktest.functional;
-import java.io.File;
-
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
import com.android.mediaframeworktest.MediaFrameworkTest;
+import java.io.File;
+
/*
* System tests for the handling of mime type in the media framework.
*
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java
index 55a1545422fd..d7d1875f95e8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java
@@ -16,17 +16,14 @@
package com.android.mediaframeworktest.functional;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
-
import android.media.MediaPlayer;
import android.os.Parcel;
+import android.test.ActivityInstrumentationTestCase2;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
import java.util.Calendar;
import java.util.Random;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioEffectTest.java
index ab78714cb56e..6b8cbe97f404 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioEffectTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioEffectTest.java
@@ -16,28 +16,26 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.AudioEffect;
import android.media.AudioFormat;
import android.media.AudioManager;
-import android.media.AudioTrack;
import android.media.AudioRecord;
-import android.media.audiofx.EnvironmentalReverb;
-import android.media.audiofx.Equalizer;
+import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
-
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.Equalizer;
import android.os.Looper;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import java.nio.ByteOrder;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.UUID;
/**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java
index 3a332c67c579..25288e11d153 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java
@@ -16,16 +16,16 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
import android.content.Context;
import android.media.AudioManager;
-import android.media.MediaPlayer;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
/**
* Junit / Instrumentation test case for the media AudioManager api
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java
index eac5c28853d0..107b51d7a6f2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java
@@ -16,17 +16,15 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
/**
* Junit / Instrumentation test case for the media AudioTrack api
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
index 1fa5c0d33cc9..c2dd246b9ae6 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
@@ -16,27 +16,13 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-import com.android.mediaframeworktest.functional.EnergyProbe;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.AudioEffect;
-import android.media.AudioManager;
import android.media.audiofx.BassBoost;
-import android.media.audiofx.Visualizer;
-import android.media.MediaPlayer;
-
-import android.os.Looper;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
-import java.util.UUID;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
/**
* Junit / Instrumentation test case for the media AudioTrack api
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java
index e788c17e0c9f..4bbd9130509d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java
@@ -16,26 +16,20 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-import com.android.mediaframeworktest.functional.EnergyProbe;
import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.AudioEffect;
import android.media.AudioManager;
-import android.media.audiofx.EnvironmentalReverb;
-import android.media.audiofx.Visualizer;
import android.media.MediaPlayer;
-
-import android.os.Looper;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.EnvironmentalReverb;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.functional.EnergyProbe;
+
import java.util.UUID;
/**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
index da9089d35369..a43f7616398b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
@@ -16,27 +16,13 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-import com.android.mediaframeworktest.functional.EnergyProbe;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.AudioEffect;
-import android.media.AudioManager;
import android.media.audiofx.Equalizer;
-import android.media.audiofx.Visualizer;
-import android.media.MediaPlayer;
-
-import android.os.Looper;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
-import java.util.UUID;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
/**
* Junit / Instrumentation test case for the media AudioTrack api
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java
index bc9c48d9fd55..9d3cf7956784 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java
@@ -16,26 +16,20 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-import com.android.mediaframeworktest.functional.EnergyProbe;
import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.AudioEffect;
import android.media.AudioManager;
-import android.media.audiofx.PresetReverb;
-import android.media.audiofx.Visualizer;
import android.media.MediaPlayer;
-
-import android.os.Looper;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.PresetReverb;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.functional.EnergyProbe;
+
import java.util.UUID;
/**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
index 122545f06da6..144656c455e9 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
@@ -16,27 +16,13 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-import com.android.mediaframeworktest.functional.EnergyProbe;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.AudioEffect;
-import android.media.AudioManager;
import android.media.audiofx.Virtualizer;
-import android.media.audiofx.Visualizer;
-import android.media.MediaPlayer;
-
-import android.os.Looper;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
-import java.util.UUID;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
/**
* Junit / Instrumentation test case for the media AudioTrack api
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java
index abf85d7e47ac..76449542ed49 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java
@@ -16,24 +16,20 @@
package com.android.mediaframeworktest.functional.audio;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.AudioEffect;
import android.media.AudioManager;
-import android.media.audiofx.Visualizer;
import android.media.MediaPlayer;
-
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.Visualizer;
import android.os.Looper;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+
import java.util.UUID;
/**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/SimTonesTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/SimTonesTest.java
index aaf992cc07a7..ae4207410502 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/SimTonesTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/SimTonesTest.java
@@ -17,12 +17,13 @@
package com.android.mediaframeworktest.functional.audio;
// import android.content.Resources;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.functional.TonesAutoTest;
-
import android.content.Context;
import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.functional.TonesAutoTest;
/**
* Junit / Instrumentation test case for the SIM tone generator
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraFunctionalTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraFunctionalTest.java
index 9c08d480ca3a..bf5e816766e3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraFunctionalTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraFunctionalTest.java
@@ -16,22 +16,23 @@
package com.android.mediaframeworktest.functional.camera;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.helpers.CameraTestHelper;
-
-import java.io.Writer;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.List;
-
import android.hardware.Camera.Parameters;
import android.os.Handler;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.view.SurfaceHolder;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.helpers.CameraTestHelper;
+
+import java.io.Writer;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
/**
* Junit / Instrumentation test case for the following camera APIs:
* - flash
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraPairwiseTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraPairwiseTest.java
index f9d49644488b..45c95e0ec2a0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraPairwiseTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/camera/CameraPairwiseTest.java
@@ -20,17 +20,18 @@ import android.hardware.Camera;
import android.os.Handler;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.view.SurfaceHolder;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.List;
+import androidx.test.filters.LargeTest;
import com.android.mediaframeworktest.MediaFrameworkTest;
import com.android.mediaframeworktest.helpers.CameraTestHelper;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
/**
* Junit / Instrumentation test case for camera API pairwise testing
* Settings tested against: flash mode, exposure compensation, white balance,
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
index 7be2707cc97c..8739820f4a09 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
@@ -16,19 +16,17 @@
package com.android.mediaframeworktest.functional.mediaplayback;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-import com.android.mediaframeworktest.MediaProfileReader;
-import com.android.mediaframeworktest.functional.CodecTest;
-
import android.content.Context;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
-import java.io.File;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.MediaProfileReader;
+import com.android.mediaframeworktest.functional.CodecTest;
/**
* Junit / Instrumentation test case for the media player api
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
index 35540e358c8b..3c456fb96f37 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
@@ -16,37 +16,32 @@
package com.android.mediaframeworktest.functional.mediarecorder;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaNames;
-
-import java.io.*;
-
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.Typeface;
import android.hardware.Camera;
+import android.media.EncoderCapabilities.AudioEncoderCap;
+import android.media.EncoderCapabilities.VideoEncoderCap;
import android.media.MediaCodec;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
-import android.media.EncoderCapabilities;
-import android.media.EncoderCapabilities.VideoEncoderCap;
-import android.media.EncoderCapabilities.AudioEncoderCap;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import com.android.mediaframeworktest.MediaProfileReader;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
import com.android.mediaframeworktest.MediaFrameworkTestRunner;
+import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.MediaProfileReader;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+import java.io.*;
import java.util.List;
-
/**
* Junit / Instrumentation test case for the media recorder api
*/
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index cc7a7d5bb9dc..e89becdec8ed 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -31,9 +31,10 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import androidx.test.filters.SmallTest;
+
/**
* <p>
* Junit / Instrumentation test case for the camera2 api
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 9d09dcc5c440..eaa5a856eb0e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -46,10 +46,11 @@ import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.view.Surface;
+import androidx.test.filters.SmallTest;
+
import com.android.mediaframeworktest.MediaFrameworkIntegrationTestRunner;
import org.mockito.ArgumentCaptor;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 8c05725a9ef1..0154d6a0e2e2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -16,36 +16,33 @@
package com.android.mediaframeworktest.performance;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaFrameworkPerfTestRunner;
-import com.android.mediaframeworktest.MediaNames;
-import com.android.mediaframeworktest.MediaTestUtil;
-
-import android.database.sqlite.SQLiteDatabase;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.media.CamcorderProfile;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
-import android.media.EncoderCapabilities.VideoEncoderCap;
import android.os.ConditionVariable;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.view.SurfaceHolder;
-import java.util.List;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkPerfTestRunner;
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.MediaProfileReader;
+import com.android.mediaframeworktest.MediaTestUtil;
+
import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.BufferedWriter;
-
-import com.android.mediaframeworktest.MediaProfileReader;
/**
* Junit / Instrumentation - performance measurement for media player and
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
index dc8da4868df3..a47d8bc2f323 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
@@ -43,13 +43,13 @@ import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.SystemClock;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.util.Range;
import android.util.Size;
import android.view.Surface;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
index a26ee2d1a668..002a143bd8bd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
@@ -16,71 +16,34 @@
package com.android.mediaframeworktest.stress;
-import com.android.ex.camera2.blocking.BlockingSessionCallback;
-import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
-import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
-import com.android.mediaframeworktest.helpers.Camera2Focuser;
-import com.android.mediaframeworktest.helpers.CameraTestUtils;
-import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_1080P;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_2160P;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
import android.graphics.ImageFormat;
-import android.graphics.Point;
-import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
-import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.DngCreator;
-import android.hardware.camera2.params.MeteringRectangle;
-import android.media.Image;
-import android.media.ImageReader;
import android.media.CamcorderProfile;
-import android.media.MediaExtractor;
-import android.media.MediaFormat;
+import android.media.ImageReader;
import android.media.MediaRecorder;
-import android.os.ConditionVariable;
-import android.os.Environment;
import android.util.Log;
-import android.util.Pair;
-import android.util.Rational;
+import android.util.Range;
import android.util.Size;
import android.view.Surface;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.util.Range;
-
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashMap;
-
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.basicValidateJpegImage;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
-import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_1080P;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_2160P;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
-import com.android.mediaframeworktest.helpers.CameraTestUtils;
+import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
-import junit.framework.AssertionFailedError;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
/**
* <p>Tests Back/Front camera switching and Camera/Video modes witching.</p>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
index 74244b9745f0..9883df2e3b86 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
@@ -16,6 +16,16 @@
package com.android.mediaframeworktest.stress;
+import android.hardware.Camera.Parameters;
+import android.os.Handler;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
import com.android.mediaframeworktest.MediaFrameworkTest;
import com.android.mediaframeworktest.helpers.CameraTestHelper;
@@ -23,19 +33,9 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
+import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-import java.util.List;
-
-import android.hardware.Camera.Parameters;
-import android.os.Handler;
-import android.os.Looper;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.view.SurfaceHolder;
-
-import androidx.test.InstrumentationRegistry;
/**
* Junit / Instrumentation test case for the following camera APIs:
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java
index 6a820ecf3b30..cb69e1b461d1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java
@@ -16,16 +16,16 @@
package com.android.mediaframeworktest.stress;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaPlayerStressTestRunner;
-
import android.os.Bundle;
import android.os.Environment;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.MediaPlayerStressTestRunner;
import com.android.mediaframeworktest.functional.CodecTest;
import java.io.BufferedReader;
@@ -34,7 +34,6 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Writer;
-
import java.util.ArrayList;
import java.util.List;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
index 4221f1bad30c..221881a9ecb9 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
@@ -16,16 +16,16 @@
package com.android.mediaframeworktest.stress;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-import com.android.mediaframeworktest.MediaPlayerStressTestRunner;
-
import android.os.Bundle;
import android.os.Environment;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.MediaPlayerStressTestRunner;
import com.android.mediaframeworktest.functional.CodecTest;
import java.io.BufferedWriter;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index 199f17970890..47bd63397448 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -17,16 +17,6 @@
package com.android.mediaframeworktest.stress;
-import com.android.mediaframeworktest.MediaFrameworkTest;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaPlayer;
@@ -35,11 +25,22 @@ import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.view.SurfaceHolder;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
import com.android.mediaframeworktest.MediaRecorderStressTestRunner;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
/**
* Junit / Instrumentation test case for the media player api
*/
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/template/AudioTestHarnessTemplateAndroidTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/template/AudioTestHarnessTemplateAndroidTest.java
index 6c2a3f72edc8..fec108f9a20b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/template/AudioTestHarnessTemplateAndroidTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/template/AudioTestHarnessTemplateAndroidTest.java
@@ -20,9 +20,10 @@ import android.media.AudioAttributes;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+
import com.android.mediaframeworktest.AudioTestHarnessTemplateRunner;
import com.android.mediaframeworktest.MediaFrameworkTest;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index c814eba7d187..964da4527917 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -16,12 +16,10 @@
package com.android.mediaframeworktest.unit;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Range;
-import android.util.Rational;
-import android.util.SizeF;
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+
+import static com.android.mediaframeworktest.unit.ByteArrayHelpers.*;
+
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.PointF;
@@ -31,7 +29,6 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
-import android.util.Size;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
import android.hardware.camera2.params.ColorSpaceTransform;
@@ -45,9 +42,14 @@ import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.TonemapCurve;
import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Rational;
+import android.util.Size;
+import android.util.SizeF;
-import static android.hardware.camera2.impl.CameraMetadataNative.*;
-import static com.android.mediaframeworktest.unit.ByteArrayHelpers.*;
+import androidx.test.filters.SmallTest;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
index 1b765eabd1c8..924ad476bb35 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
@@ -20,8 +20,7 @@ import static android.hardware.camera2.utils.TypeReference.*;
import android.hardware.camera2.utils.TypeReference;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
+import androidx.test.filters.SmallTest;
import java.lang.reflect.Type;
import java.util.List;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java
index b648763db9e9..6cb8b54617e5 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsUncheckedThrowTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.utils.UncheckedThrow;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import junit.framework.Assert;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
index 2cb5704f5c2d..d9fc88de3f8a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
@@ -24,7 +24,8 @@ import android.media.Image.Plane;
import android.media.ImageReader;
import android.media.ImageReader.OnImageAvailableListener;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
public class ImageReaderTest extends AndroidTestCase {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index ea0494f2a8f1..65264d30f04f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -36,7 +36,8 @@ import android.provider.MediaStore.Files;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index e3d389737bb0..80edf8a4bd0a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -26,10 +26,10 @@ import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.media.playback.flags.Flags;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java
index 37dd4b5330c5..06c527d3e584 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetDurationStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetDurationStateUnitTest.java
index 48fd16c3f4b2..5a3604401e3b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetDurationStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetDurationStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoHeightStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoHeightStateUnitTest.java
index 6d3c0831418e..f86f77e26187 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoHeightStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoHeightStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoWidthStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoWidthStateUnitTest.java
index 198439cd921c..fb31edb3b720 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoWidthStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetVideoWidthStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerIsPlayingStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerIsPlayingStateUnitTest.java
index b9c63fd5a4c2..e1c992ebda98 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerIsPlayingStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerIsPlayingStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
index bfa3976f5867..7aed3b7a53c6 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
@@ -18,10 +18,9 @@ package com.android.mediaframeworktest.unit;
import android.media.Metadata;
import android.os.Parcel;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import java.util.Calendar;
+import androidx.test.filters.SmallTest;
+
import java.util.Date;
/*
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerPauseStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerPauseStateUnitTest.java
index 7a967927202a..aa4bfe67e941 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerPauseStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerPauseStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerResetStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerResetStateUnitTest.java
index 2497cd7dec3e..d8651f5681b8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerResetStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerResetStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSeekToStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSeekToStateUnitTest.java
index 991fe9cc5041..3bcde7f2bd3e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSeekToStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSeekToStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetAudioStreamTypeStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetAudioStreamTypeStateUnitTest.java
index b984514452d4..36e93c0fb40e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetAudioStreamTypeStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetAudioStreamTypeStateUnitTest.java
@@ -16,10 +16,11 @@
package com.android.mediaframeworktest.unit;
-import android.media.MediaPlayer;
import android.media.AudioManager;
+import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetLoopingStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetLoopingStateUnitTest.java
index 17c9d8c1979f..adb56d0f8335 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetLoopingStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetLoopingStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetVolumeStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetVolumeStateUnitTest.java
index a149565039f4..84691da19ea8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetVolumeStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerSetVolumeStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStartStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStartStateUnitTest.java
index 68c8e42a1a21..a0999d894a2c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStartStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStartStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStopStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStopStateUnitTest.java
index ab8519a760b9..b0673a87dd71 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStopStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerStopStateUnitTest.java
@@ -18,7 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java
index 134144d005b9..8299a7e6dd0f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java
@@ -18,8 +18,9 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+
import java.io.IOException;
/**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java
index cae9e31c3cad..1e4c06025b8b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java
@@ -18,8 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java
index 4b5a8183087b..b3e9009dbdd4 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java
@@ -18,8 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java
index f8ab48cf178e..3b56e6f21484 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java
@@ -18,8 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java
index 712a7586f53f..0980802c868c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java
@@ -18,8 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java
index cacdd87557c0..3a4a7c732be3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java
@@ -18,8 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java
index d1232fcfba40..192c8966310e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java
@@ -18,8 +18,8 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
/**
* Unit test class to test the set of valid and invalid states that
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java
index 91100aef07bf..093ed88f4b9c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java
@@ -18,10 +18,10 @@ package com.android.mediaframeworktest.unit;
import android.media.MediaRecorder;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
+import androidx.test.filters.MediumTest;
+
/**
* Unit test class to test the set of valid and invalid states that
* MediaRecorder.stop() method can be called.
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java
index 9dd2732cb0f3..f54f06dae291 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java
@@ -16,10 +16,11 @@
package com.android.mediaframeworktest.unit;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Range;
import android.util.Rational;
+import androidx.test.filters.SmallTest;
+
/**
* <pre>
* adb shell am instrument \
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
index 1bb7db9034f3..a94b5af527cf 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
@@ -16,9 +16,12 @@
package com.android.mediaframeworktest.unit;
-import android.test.suitebuilder.annotation.SmallTest;
+import static android.util.Rational.*;
+
import android.util.Rational;
+import androidx.test.filters.SmallTest;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -28,8 +31,6 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
-import static android.util.Rational.*;
-
/**
* <pre>
* adb shell am instrument \
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
index f578e46ea4f1..cd564b2d1fd3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
@@ -19,9 +19,10 @@ package com.android.mediaframeworktest.unit;
import android.graphics.ImageFormat;
import android.hardware.camera2.utils.SurfaceUtils;
import android.media.ImageReader;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.Surface;
+import androidx.test.filters.SmallTest;
+
import junit.framework.Assert;
public class SurfaceUtilsTest extends junit.framework.TestCase {
diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp
index c9a886415c6d..fd5f19535537 100644
--- a/media/tests/projection/Android.bp
+++ b/media/tests/projection/Android.bp
@@ -3,6 +3,7 @@
//########################################################################
package {
+ default_team: "trendy_team_lse_desktop_os_experience",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 7f3792d06795..752ebdf3d0e3 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -98,6 +98,7 @@ cc_library_shared {
"libpowermanager",
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
+ "android.os.flags-aconfig-cc",
"libnativedisplay",
],
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 9d0221a3ae68..c0db089253e7 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -268,9 +268,9 @@ package android.nfc.cardemulation {
ctor public PollingFrame(int, @Nullable byte[], int, int);
method public int describeContents();
method @NonNull public byte[] getData();
- method public int getGain();
method public int getTimestamp();
method public int getType();
+ method public int getVendorSpecificGain();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.PollingFrame> CREATOR;
field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_A = 65; // 0x41
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
index 3383f3bd4e7a..29d7bdf37fe4 100644
--- a/nfc/java/android/nfc/cardemulation/PollingFrame.java
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -157,7 +157,7 @@ public final class PollingFrame implements Parcelable{
mType = frame.getInt(KEY_POLLING_LOOP_TYPE);
byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA);
mData = (data == null) ? new byte[0] : data;
- mGain = frame.getByte(KEY_POLLING_LOOP_GAIN);
+ mGain = frame.getInt(KEY_POLLING_LOOP_GAIN, -1);
mTimestamp = frame.getInt(KEY_POLLING_LOOP_TIMESTAMP);
}
@@ -194,14 +194,15 @@ public final class PollingFrame implements Parcelable{
/**
* Returns the gain representing the field strength of the NFC field when this polling loop
* frame was observed.
+ * @return the gain or -1 if there is no gain measurement associated with this frame.
*/
- public int getGain() {
+ public int getVendorSpecificGain() {
return mGain;
}
/**
* Returns the timestamp of when the polling loop frame was observed in milliseconds. These
- * timestamps are relative and not absolute and should only be used fro comparing the timing of
+ * timestamps are relative and not absolute and should only be used for comparing the timing of
* frames relative to each other.
* @return the timestamp in milliseconds
*/
@@ -227,7 +228,9 @@ public final class PollingFrame implements Parcelable{
public Bundle toBundle() {
Bundle frame = new Bundle();
frame.putInt(KEY_POLLING_LOOP_TYPE, getType());
- frame.putByte(KEY_POLLING_LOOP_GAIN, (byte) getGain());
+ if (getVendorSpecificGain() != -1) {
+ frame.putInt(KEY_POLLING_LOOP_GAIN, (byte) getVendorSpecificGain());
+ }
frame.putByteArray(KEY_POLLING_LOOP_DATA, getData());
frame.putInt(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
return frame;
@@ -236,7 +239,7 @@ public final class PollingFrame implements Parcelable{
@Override
public String toString() {
return "PollingFrame { Type: " + (char) getType()
- + ", gain: " + getGain()
+ + ", gain: " + getVendorSpecificGain()
+ ", timestamp: " + Integer.toUnsignedString(getTimestamp())
+ ", data: [" + HexFormat.ofDelimiter(" ").formatHex(getData()) + "] }";
}
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index bc3ad0615ada..d910f2fc9904 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -72,6 +72,6 @@
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strøm apper og andre systemfunksjoner fra telefonen"</string>
<string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Se en liste over tilgjengelige enheter, og kontroller hvilken enhet som strømmer eller caster lyd eller video fra andre apper"</string>
- <string name="device_type" product="default" msgid="8268703872070046263">"telefonen"</string>
- <string name="device_type" product="tablet" msgid="5038791954983067774">"nettbrettet"</string>
+ <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
+ <string name="device_type" product="tablet" msgid="5038791954983067774">"nettbrett"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 66282dc209ed..4c1f6313c3a0 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -27,13 +27,13 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT;
-import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_PERMISSIONS;
-import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_NAMES;
import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICONS;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_NAMES;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_PERMISSIONS;
import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_SUMMARIES;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_TITLES;
import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_PROFILES;
import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_SELF_MANAGED_PROFILES;
-import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_TITLES;
import static com.android.companiondevicemanager.Utils.getApplicationLabel;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getIcon;
@@ -68,6 +68,7 @@ import android.text.Spanned;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -697,14 +698,15 @@ public class CompanionDeviceActivity extends FragmentActivity implements
disableButtons();
+ LinearLayoutManager permissionListLayoutManager =
+ (LinearLayoutManager) mPermissionListRecyclerView
+ .getLayoutManager();
+
// Enable buttons once users scroll down to the bottom of the permission list.
mPermissionListRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
- public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
- super.onScrollStateChanged(recyclerView, newState);
- if (!recyclerView.canScrollVertically(1)) {
- enableButtons();
- }
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ enableAllowButtonIfNeeded(permissionListLayoutManager);
}
});
// Enable buttons if last item in the permission list is visible to the users when
@@ -713,26 +715,36 @@ public class CompanionDeviceActivity extends FragmentActivity implements
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
- LinearLayoutManager layoutManager =
- (LinearLayoutManager) mPermissionListRecyclerView
- .getLayoutManager();
- int lastVisibleItemPosition =
- layoutManager.findLastCompletelyVisibleItemPosition();
- int numItems = mPermissionListRecyclerView.getAdapter().getItemCount();
-
- if (lastVisibleItemPosition >= numItems - 1) {
- enableButtons();
- }
-
+ enableAllowButtonIfNeeded(permissionListLayoutManager);
mPermissionListRecyclerView.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
});
+ // Set accessibility for the recyclerView that to be able scroll up/down for voice access.
+ mPermissionListRecyclerView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
+ }
+ });
+
mConstraintList.setVisibility(View.VISIBLE);
mPermissionListRecyclerView.setVisibility(View.VISIBLE);
}
+ // Enable the Allow button if the last element in the PermissionListRecyclerView is reached.
+ private void enableAllowButtonIfNeeded(LinearLayoutManager layoutManager) {
+ int lastVisibleItemPosition =
+ layoutManager.findLastCompletelyVisibleItemPosition();
+ int numItems = mPermissionListRecyclerView.getAdapter().getItemCount();
+
+ if (lastVisibleItemPosition >= numItems - 1) {
+ enableButtons();
+ }
+ }
+
// Disable and grey out the Allow and Don't allow buttons if the last permission in the
// permission list is not visible to the users.
private void disableButtons() {
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
deleted file mode 100644
index eb1faa0aa25c..000000000000
--- a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Type annotations for constants used in the connectivity API surface.
- *
- * The annotations are maintained in a separate class so that it can be built as
- * a separate library that other modules can build against, as Typedef should not
- * be exposed as SystemApi.
- *
- * @hide
- */
-public final class ConnectivityAnnotations {
- private ConnectivityAnnotations() {}
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER,
- ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY,
- ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE,
- })
- public @interface MultipathPreference {}
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = false, value = {
- ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED,
- ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED,
- ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED,
- })
- public @interface RestrictBackgroundStatus {}
-}
diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml
index 6c46ffca6aec..b9ed4a2564de 100644
--- a/packages/CredentialManager/res/values-af/strings.xml
+++ b/packages/CredentialManager/res/values-af/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Wagwoorde sal steeds saam met toegangsleutels beskikbaar wees terwyl ons na ’n wagwoordlose toekoms beweeg."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Kies waar om jou <xliff:g id="CREATETYPES">%1$s</xliff:g> te stoor"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Kies ’n wagwoordbestuurder om jou inligting te stoor en volgende keer vinniger aan te meld"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Skep toegangsleutel vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Stoor wagwoord vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Stoor aanmeldinligting vir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"toegangsleutel"</string>
<string name="password" msgid="6738570945182936667">"wagwoord"</string>
diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml
index 6d65fe11fd05..e1ded6ac39a7 100644
--- a/packages/CredentialManager/res/values-am/strings.xml
+++ b/packages/CredentialManager/res/values-am/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"ወደ የይለፍ ቃል የሌለው ወደፊት ስንሄድ የይለፍ ቃላት ከይለፍ ቁልፎች ጎን ለጎን ይገኛሉ።"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"የእርስዎን <xliff:g id="CREATETYPES">%1$s</xliff:g> የት እንደሚያስቀምጡ ይምረጡ"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"መረጃዎን ለማስቀመጥ እና በቀጣይ ጊዜ በፍጥነት በመለያ ለመግባት የሚስጥር ቁልፍ አስተዳዳሪን ይምረጡ"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ለ<xliff:g id="APPNAME">%1$s</xliff:g> የይለፍ ቁልፍ ይፈጠር?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"ለ<xliff:g id="APPNAME">%1$s</xliff:g> የይለፍ ቃል ይቀመጥ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"ለ<xliff:g id="APPNAME">%1$s</xliff:g> የመግቢያ መረጃ ይቀመጥ?"</string>
<string name="passkey" msgid="632353688396759522">"የይለፍ ቁልፍ"</string>
<string name="password" msgid="6738570945182936667">"የይለፍ ቃል"</string>
diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml
index 654d5b6917aa..be55469be98d 100644
--- a/packages/CredentialManager/res/values-ar/strings.xml
+++ b/packages/CredentialManager/res/values-ar/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"بينما ننطلق نحو مستقبل بدون كلمات مرور، ستظل كلمات المرور متوفّرة إلى جانب مفاتيح المرور."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"اختيار المكان الذي تريد حفظ <xliff:g id="CREATETYPES">%1$s</xliff:g> فيه"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"اختَر مدير كلمات مرور لحفظ معلوماتك وتسجيل الدخول بشكل أسرع في المرة القادمة."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"هل تريد إنشاء مفتاح مرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"هل تريد حفظ كلمة المرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"هل تريد حفظ معلومات تسجيل الدخول لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
<string name="passkey" msgid="632353688396759522">"مفتاح المرور"</string>
<string name="password" msgid="6738570945182936667">"كلمة المرور"</string>
diff --git a/packages/CredentialManager/res/values-as/strings.xml b/packages/CredentialManager/res/values-as/strings.xml
index 9ab41ddb214c..6b02ea71dedc 100644
--- a/packages/CredentialManager/res/values-as/strings.xml
+++ b/packages/CredentialManager/res/values-as/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"আমি পাছৱৰ্ডবিহীন ভৱিষ্যতৰ দিশে আগবঢ়াৰ লগে লগে পাছকীৰ লগতে পাছৱৰ্ডসমূহো উপলব্ধ হ’ব।"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"আপোনাৰ <xliff:g id="CREATETYPES">%1$s</xliff:g> ক’ত ছেভ কৰিব লাগে সেয়া বাছনি কৰক"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"আপোনাৰ তথ্য ছেভ কৰি পৰৱৰ্তী সময়ত দ্ৰুতভাৱে ছাইন ইন কৰিবলৈ এটা পাছৱৰ্ড পৰিচালক বাছনি কৰক"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>ৰ বাবে পাছকী সৃষ্টি কৰিবনে?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g>ৰ বাবে পাছৱৰ্ড ছেভ কৰিবনে?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g>ৰ বাবে ছাইন ইনৰ তথ্য ছেভ কৰিবনে?"</string>
<string name="passkey" msgid="632353688396759522">"পাছকী"</string>
<string name="password" msgid="6738570945182936667">"পাছৱৰ্ড"</string>
diff --git a/packages/CredentialManager/res/values-az/strings.xml b/packages/CredentialManager/res/values-az/strings.xml
index 67efc8bdde48..caef7270860e 100644
--- a/packages/CredentialManager/res/values-az/strings.xml
+++ b/packages/CredentialManager/res/values-az/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Parolsuz gələcəyə doğru irəlilədikcə parollar hələ də açarlar ilə yanaşı əlçatan olacaq."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> elementinin saxlanacağı yeri seçin"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Məlumatlarınızı yadda saxlamaq və növbəti dəfə daha sürətli daxil olmaq üçün parol meneceri seçin"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> üçün giriş açarı yaradılsın?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> üçün parol yadda saxlanılsın?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> üçün giriş məlumatları yadda saxlansın?"</string>
<string name="passkey" msgid="632353688396759522">"açar"</string>
<string name="password" msgid="6738570945182936667">"parol"</string>
diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
index e01e8a3613c9..0248a08e2a7b 100644
--- a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Kako se krećemo ka budućnosti bez lozinki, lozinke će i dalje biti dostupne uz pristupne kodove."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Odaberite gde ćete sačuvati: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Izaberite menadžera lozinki da biste sačuvali podatke i brže se prijavili sledeći put"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Želite da napravite pristupni kôd za: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Želite da sačuvate lozinku za: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Želite da sačuvate podatke za prijavljivanje za: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"pristupni kôd"</string>
<string name="password" msgid="6738570945182936667">"lozinka"</string>
diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml
index e7fe62242a92..cc841d19adbd 100644
--- a/packages/CredentialManager/res/values-be/strings.xml
+++ b/packages/CredentialManager/res/values-be/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Хоць мы ўжо рухаемся ў бок будучыні без выкарыстання пароляў, яны па-ранейшаму застануцца даступнымі нароўні з ключамі доступу."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Выберыце, куды захаваць <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Выберыце менеджар пароляў, каб захаваць свае даныя і забяспечыць хуткі ўваход у наступныя разы"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Стварыце ключ доступу да праграмы \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Захаваць пароль для праграмы \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Захаваць інфармацыю пра спосаб уваходу ў праграму \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml
index 9b5255efaf73..e7027c1d45da 100644
--- a/packages/CredentialManager/res/values-bg/strings.xml
+++ b/packages/CredentialManager/res/values-bg/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Паролите ще продължат да са налице заедно с ключовете за достъп по пътя ни към бъдеще без пароли."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Изберете къде да запазите своите <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Изберете мениджър на пароли, в който да се запазят данните ви, така че следващия път да влезете по-бързо в профила си"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Да се създаде ли код за достъп за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Да се запази ли паролата за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Да се запазят ли данните за вход за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"код за достъп"</string>
<string name="password" msgid="6738570945182936667">"парола"</string>
diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml
index 981431c57593..49eb68c8216a 100644
--- a/packages/CredentialManager/res/values-bn/strings.xml
+++ b/packages/CredentialManager/res/values-bn/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"আমরা পাসওয়ার্ডবিহীন ভবিষ্যতের দিকে এগিয়ে গেলেও, এখনও \'পাসকী\'-এর পাশাপাশি পাসওয়ার্ড ব্যবহার করা যাবে।"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"আপনার <xliff:g id="CREATETYPES">%1$s</xliff:g> কোথায় সেভ করবেন তা বেছে নিন"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"আপনার তথ্য সেভ করতে একটি Password Manager বেছে নিন এবং পরের বার আরও দ্রুত সাইন-ইন করুন"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>-এর জন্য \'পাসকী\' তৈরি করবেন?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g>-এর জন্য পাসওয়ার্ড সেভ করবেন?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g>-এর জন্য সাইন-ইন সংক্রান্ত তথ্য সেভ করবেন?"</string>
<string name="passkey" msgid="632353688396759522">"পাসকী"</string>
<string name="password" msgid="6738570945182936667">"পাসওয়ার্ড"</string>
diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml
index 2e516250ee6a..afa4882a292e 100644
--- a/packages/CredentialManager/res/values-bs/strings.xml
+++ b/packages/CredentialManager/res/values-bs/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Kako se krećemo prema budućnosti bez lozinki, lozinke će i dalje biti dostupne uz pristupne ključeve."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Odaberite gdje će se pohranjivati <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Odaberite upravitelja lozinki da sačuvate svoje informacije i brže se prijavite sljedeći put"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Kreirati pristupni ključ za aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Sačuvati lozinku za aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Sačuvati informacije o prijavi za aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
<string name="password" msgid="6738570945182936667">"lozinka"</string>
diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml
index 97d758cc47bf..c937c09cd442 100644
--- a/packages/CredentialManager/res/values-ca/strings.xml
+++ b/packages/CredentialManager/res/values-ca/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Tot i que avancem cap a un futur sense contrasenyes, continuaran estant disponibles juntament amb les claus d\'accés."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Tria on vols desar les <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un gestor de contrasenyes per desar la teva informació i iniciar la sessió més ràpidament la pròxima vegada"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vols crear la clau d\'accés per a <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Vols desar la contrasenya per a <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vols desar la informació d\'inici de sessió per a <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clau d\'accés"</string>
<string name="password" msgid="6738570945182936667">"contrasenya"</string>
diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml
index c317c9228a65..06a81b02f7da 100644
--- a/packages/CredentialManager/res/values-cs/strings.xml
+++ b/packages/CredentialManager/res/values-cs/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Ačkoliv směřujeme k budoucnosti bez hesel, vedle přístupových klíčů budou stále k dispozici i hesla."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Určete, kam ukládat <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Vyberte správce hesel k uložení svých údajů, abyste se příště mohli přihlásit rychleji"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vytvořit přístupový klíč pro aplikaci <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Uložit heslo pro aplikaci <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Uložit přihlašovací údaje pro aplikaci <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"přístupový klíč"</string>
<string name="password" msgid="6738570945182936667">"heslo"</string>
diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml
index cc6b12a0653d..207c33c7a6be 100644
--- a/packages/CredentialManager/res/values-da/strings.xml
+++ b/packages/CredentialManager/res/values-da/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Selvom vi nærmer os en fremtid, hvor adgangskoder er mindre fremtrædende, kan de stadig bruges i samspil med adgangsnøgler."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Vælg, hvor du vil gemme dine <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Vælg en adgangskodeadministrator for at gemme dine oplysninger, så du kan logge ind hurtigere næste gang"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vil du oprette en adgangsnøgle til <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Vil du gemme adgangskoden til <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vil du gemme loginoplysningerne til <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"adgangsnøgle"</string>
<string name="password" msgid="6738570945182936667">"adgangskode"</string>
diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml
index 0dbde65becc6..38fa3e9a4faa 100644
--- a/packages/CredentialManager/res/values-de/strings.xml
+++ b/packages/CredentialManager/res/values-de/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Auch wenn wir uns auf eine passwortlose Zukunft zubewegen, werden neben Passkeys weiter Passwörter verfügbar sein."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Wähle aus, wo deine <xliff:g id="CREATETYPES">%1$s</xliff:g> gespeichert werden sollen"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Du kannst einen Passwortmanager auswählen, um deine Anmeldedaten zu speichern, damit du dich nächstes Mal schneller anmelden kannst"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Passkey für <xliff:g id="APPNAME">%1$s</xliff:g> erstellen?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Passwort für <xliff:g id="APPNAME">%1$s</xliff:g> speichern?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Anmeldedaten für <xliff:g id="APPNAME">%1$s</xliff:g> speichern?"</string>
<string name="passkey" msgid="632353688396759522">"Passkey"</string>
<string name="password" msgid="6738570945182936667">"Passwort"</string>
diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml
index c2a28631a7dc..509cfe6d34f3 100644
--- a/packages/CredentialManager/res/values-el/strings.xml
+++ b/packages/CredentialManager/res/values-el/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Καθώς κινούμαστε προς ένα μέλλον χωρίς κωδικούς πρόσβασης, οι κωδικοί πρόσβασης θα εξακολουθούν να είναι διαθέσιμοι μαζί με τα κλειδιά πρόσβασης."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Επιλέξτε πού θα αποθηκεύονται τα <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Επιλέξτε ένα πρόγραμμα διαχείρισης κωδικών πρόσβασης για να αποθηκεύσετε τα στοιχεία σας και να συνδεθείτε πιο γρήγορα την επόμενη φορά."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Δημιουργία κλειδιού πρόσβασης για <xliff:g id="APPNAME">%1$s</xliff:g>;"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Αποθήκευση κωδικού πρόσβασης για <xliff:g id="APPNAME">%1$s</xliff:g>;"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Αποθήκευση στοιχείων σύνδεσης για <xliff:g id="APPNAME">%1$s</xliff:g>;"</string>
<string name="passkey" msgid="632353688396759522">"κλειδί πρόσβασης"</string>
<string name="password" msgid="6738570945182936667">"κωδικός πρόσβασης"</string>
diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml
index 7ae0583bcb45..cd63b41bc331 100644
--- a/packages/CredentialManager/res/values-en-rAU/strings.xml
+++ b/packages/CredentialManager/res/values-en-rAU/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Save password for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Save sign-in info for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
diff --git a/packages/CredentialManager/res/values-en-rCA/strings.xml b/packages/CredentialManager/res/values-en-rCA/strings.xml
index 195f12f8ee4c..ff1de20583bd 100644
--- a/packages/CredentialManager/res/values-en-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-en-rCA/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Save password for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Save sign-in info for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml
index 7ae0583bcb45..cd63b41bc331 100644
--- a/packages/CredentialManager/res/values-en-rGB/strings.xml
+++ b/packages/CredentialManager/res/values-en-rGB/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Save password for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Save sign-in info for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml
index 7ae0583bcb45..cd63b41bc331 100644
--- a/packages/CredentialManager/res/values-en-rIN/strings.xml
+++ b/packages/CredentialManager/res/values-en-rIN/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"As we move towards a passwordless future, passwords will still be available alongside passkeys."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choose where to save your <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Select a password manager to save your info and sign in faster next time"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Create passkey for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Save password for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Save sign-in info for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
diff --git a/packages/CredentialManager/res/values-en-rXC/strings.xml b/packages/CredentialManager/res/values-en-rXC/strings.xml
index 5949607c318b..8ffe0016fa23 100644
--- a/packages/CredentialManager/res/values-en-rXC/strings.xml
+++ b/packages/CredentialManager/res/values-en-rXC/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎As we move towards a passwordless future, passwords will still be available alongside passkeys.‎‏‎‎‏‎"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‎‎Choose where to save your ‎‏‎‎‏‏‎<xliff:g id="CREATETYPES">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‏‎‎Select a password manager to save your info and sign in faster next time‎‏‎‎‏‎"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎Create passkey for ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‏‏‎Save password for ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎Save sign-in info for ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
<string name="passkey" msgid="632353688396759522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎passkey‎‏‎‎‏‎"</string>
<string name="password" msgid="6738570945182936667">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎password‎‏‎‎‏‎"</string>
diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml
index f42693edcc73..29fb64db2ed2 100644
--- a/packages/CredentialManager/res/values-es-rUS/strings.xml
+++ b/packages/CredentialManager/res/values-es-rUS/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"A medida que avanzamos hacia un futuro sin contraseñas, estas seguirán estando disponibles junto a las llaves de acceso."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Elige dónde guardar tus <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un administrador de contraseñas para guardar tu información y acceder más rápido la próxima vez"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"¿Quieres crear una llave de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"¿Quieres guardar la contraseña para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"¿Quieres guardar la información de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contraseña"</string>
diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml
index 7e16e208a045..077ea99ab0df 100644
--- a/packages/CredentialManager/res/values-es/strings.xml
+++ b/packages/CredentialManager/res/values-es/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Aunque nos dirigimos hacia un mundo sin contraseñas, estas seguirán estando disponibles junto con las llaves de acceso."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Elige dónde guardar tus <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un gestor de contraseñas para guardar tu información e iniciar sesión más rápido la próxima vez"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"¿Crear llave de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"¿Guardar la contraseña de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"¿Guardar la información de inicio de sesión de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contraseña"</string>
diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml
index 0ae48b47c92a..6de564b9ed99 100644
--- a/packages/CredentialManager/res/values-et/strings.xml
+++ b/packages/CredentialManager/res/values-et/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Liikudes paroolivaba tuleviku poole, jäävad paroolid pääsuvõtmete kõrval siiski kättesaadavaks."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Valige, kuhu soovite oma <xliff:g id="CREATETYPES">%1$s</xliff:g> salvestada"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Valige paroolihaldur, et salvestada oma teave ja järgmisel korral kiiremini sisse logida"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Kas luua rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> jaoks pääsuvõti?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Kas salvestada rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> jaoks parool?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Kas salvestada rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> jaoks sisselogimisandmed?"</string>
<string name="passkey" msgid="632353688396759522">"pääsuvõti"</string>
<string name="password" msgid="6738570945182936667">"parool"</string>
diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml
index 8775c8782101..018968cf6d70 100644
--- a/packages/CredentialManager/res/values-eu/strings.xml
+++ b/packages/CredentialManager/res/values-eu/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Pasahitzik gabeko etorkizun baterantz goazen arren, pasahitzek sarbide-gakoen bizikide izaten jarraituko dute."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Aukeratu non gorde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Hautatu informazioa gordetzeko pasahitz-kudeatzaile bat eta hasi saioa bizkorrago hurrengoan"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> atzitzeko sarbide-gako bat sortu nahi duzu?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioko pasahitza gorde nahi duzu?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioko saioa hasteko informazioa gorde nahi duzu?"</string>
<string name="passkey" msgid="632353688396759522">"sarbide-gakoa"</string>
<string name="password" msgid="6738570945182936667">"pasahitza"</string>
diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml
index 381c61e8c01a..55a79d86a3f8 100644
--- a/packages/CredentialManager/res/values-fa/strings.xml
+++ b/packages/CredentialManager/res/values-fa/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"درحالی‌که به‌سوی آینده‌ای بی‌گذرواژه حرکت می‌کنیم، گذرواژه‌ها همچنان در کنار گذرکلیدها دردسترس خواهند بود"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"جایی را برای ذخیره کردن <xliff:g id="CREATETYPES">%1$s</xliff:g> انتخاب کنید"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"مدیر گذرواژه‌ای انتخاب کنید تا اطلاعاتتان را ذخیره کنید و دفعه بعد سریع‌تر به سیستم وارد شوید"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"برای <xliff:g id="APPNAME">%1$s</xliff:g> گذرکلید ایجاد شود؟"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"گذرواژه <xliff:g id="APPNAME">%1$s</xliff:g> ذخیره شود؟"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"اطلاعات ورود به سیستم <xliff:g id="APPNAME">%1$s</xliff:g> ذخیره شود؟"</string>
<string name="passkey" msgid="632353688396759522">"گذرکلید"</string>
<string name="password" msgid="6738570945182936667">"گذرواژه"</string>
diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml
index 3c1c5968e433..9f95720a22b4 100644
--- a/packages/CredentialManager/res/values-fi/strings.xml
+++ b/packages/CredentialManager/res/values-fi/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Kehitys kulkee kohti salasanatonta tulevaisuutta, mutta salasanat ovat edelleen käytettävissä avainkoodien ohella."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Valitse, minne <xliff:g id="CREATETYPES">%1$s</xliff:g> tallennetaan"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Valitse salasanojen ylläpitotyökalu, niin voit tallentaa tietosi ja kirjautua ensi kerralla nopeammin sisään"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Luodaanko avainkoodi (<xliff:g id="APPNAME">%1$s</xliff:g>)?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Tallennetaanko salasana (<xliff:g id="APPNAME">%1$s</xliff:g>)?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Tallennetaanko kirjautumistiedot (<xliff:g id="APPNAME">%1$s</xliff:g>)?"</string>
<string name="passkey" msgid="632353688396759522">"avainkoodi"</string>
<string name="password" msgid="6738570945182936667">"salasana"</string>
diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml
index ff95af239622..481fac9b1da6 100644
--- a/packages/CredentialManager/res/values-fr-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"À mesure que nous nous dirigeons vers un avenir sans mot de passe, ils resteront toujours utilisés parallèlement aux clés d\'accès."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choisir où enregistrer vos <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Sélectionnez un gestionnaire de mots de passe pour enregistrer vos renseignements et vous connecter plus rapidement la prochaine fois"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Créer une clé d\'accès pour <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Enregistrer le mot de passe pour <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Enregistrer les renseignements de connexion pour <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
<string name="password" msgid="6738570945182936667">"mot de passe"</string>
diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml
index d39391221713..37df7fb1e83d 100644
--- a/packages/CredentialManager/res/values-fr/strings.xml
+++ b/packages/CredentialManager/res/values-fr/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Nous nous dirigeons vers un futur sans mots de passe, mais ceux-ci resteront disponibles en plus des clés d\'accès."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Choisissez où enregistrer vos <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Sélectionnez un gestionnaire de mots de passe pour enregistrer vos informations et vous connecter plus rapidement la prochaine fois"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Créer une clé d\'accès pour <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Enregistrer le mot de passe pour <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Enregistrer les informations de connexion pour <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
<string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
<string name="password" msgid="6738570945182936667">"mot de passe"</string>
diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml
index d3c822233c17..8770465aecd6 100644
--- a/packages/CredentialManager/res/values-gl/strings.xml
+++ b/packages/CredentialManager/res/values-gl/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="4539824758261855508">"Xestor de credenciais"</string>
+ <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string>
<string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
<string name="string_more_options" msgid="2763852250269945472">"Gardar doutro xeito"</string>
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Durante este percorrido cara a un futuro sen contrasinais, estes seguirán estando dispoñibles a canda as claves de acceso."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Escolle onde queres gardar: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecciona un xestor de contrasinais para gardar a túa información e iniciar sesión máis rápido a próxima vez"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Queres crear unha clave de acceso para <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Queres gardar o contrasinal de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Queres gardar a información de inicio de sesión de <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"clave de acceso"</string>
<string name="password" msgid="6738570945182936667">"contrasinal"</string>
diff --git a/packages/CredentialManager/res/values-gu/strings.xml b/packages/CredentialManager/res/values-gu/strings.xml
index 3d4da84df23b..efc88c13a3eb 100644
--- a/packages/CredentialManager/res/values-gu/strings.xml
+++ b/packages/CredentialManager/res/values-gu/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"આપણે પાસવર્ડ રહિત ભવિષ્ય તરફ આગળ વધી રહ્યાં છીએ, છતાં પાસકીની સાથોસાથ હજી પણ પાસવર્ડ ઉપલબ્ધ રહેશે."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"તમારી <xliff:g id="CREATETYPES">%1$s</xliff:g> ક્યાં સાચવવી તે પસંદ કરો"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"તમારી માહિતી સાચવવા માટે પાસવર્ડ મેનેજર પસંદ કરો અને આગલી વખતે વધુ ઝડપથી સાઇન ઇન કરો"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> માટે પાસકી બનાવીએ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> માટે પાસવર્ડ સાચવીએ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> માટે સાઇન-ઇન કરવાની માહિતી સાચવીએ?"</string>
<string name="passkey" msgid="632353688396759522">"પાસકી"</string>
<string name="password" msgid="6738570945182936667">"પાસવર્ડ"</string>
diff --git a/packages/CredentialManager/res/values-hi/strings.xml b/packages/CredentialManager/res/values-hi/strings.xml
index 672a8523d9ed..661a4a227916 100644
--- a/packages/CredentialManager/res/values-hi/strings.xml
+++ b/packages/CredentialManager/res/values-hi/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"आने वाले समय में बिना पासवर्ड वाली टेक्नोलॉजी यानी पासकी का इस्तेमाल बढ़ेगा, हालांकि इसके साथ-साथ पासवर्ड भी इस्तेमाल किए जा सकेंगे."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"चुनें कि अपनी <xliff:g id="CREATETYPES">%1$s</xliff:g> कहां सेव करनी हैं"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"अपनी जानकारी सेव करने के लिए, पासवर्ड मैनेजर चुनें और अगली बार ज़्यादा तेज़ी से साइन इन करें"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"क्या आपको <xliff:g id="APPNAME">%1$s</xliff:g> के लिए पासकी बनानी है?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"क्या आपको <xliff:g id="APPNAME">%1$s</xliff:g> के लिए पासवर्ड सेव करना है?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"क्या आपको <xliff:g id="APPNAME">%1$s</xliff:g> के लिए साइन-इन की जानकारी सेव करनी है?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml
index 525ade3642d4..eceb1b54c247 100644
--- a/packages/CredentialManager/res/values-hr/strings.xml
+++ b/packages/CredentialManager/res/values-hr/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Kako idemo u smjeru budućnosti bez zaporki, one će i dalje biti dostupne uz pristupne ključeve."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Odaberite gdje će se spremati <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Odaberite upravitelja zaporki kako biste spremili svoje informacije i drugi se put brže prijavili"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Želite li izraditi pristupni ključ za <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Spremiti zaporku za <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Spremiti informacije o prijavi za <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
<string name="password" msgid="6738570945182936667">"zaporka"</string>
diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml
index 1a67ecb02859..3415eea3f06c 100644
--- a/packages/CredentialManager/res/values-hu/strings.xml
+++ b/packages/CredentialManager/res/values-hu/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Miközben a jelszó nélküli jövő felé haladunk, a jelszavak továbbra is rendelkezésre állnak majd az azonosítókulcsok mellett."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Válassza ki, hogy hova szeretné menteni <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Válasszon jelszókezelőt, hogy menthesse az adatait, és gyorsabban jelentkezhessen be a következő alkalommal."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Létrehoz azonosítókulcsot a következőhöz: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Szeretné elmenteni a(z) <xliff:g id="APPNAME">%1$s</xliff:g> jelszavát?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Menti a bejelentkezési adatokat a következőhöz: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"azonosítókulcs"</string>
<string name="password" msgid="6738570945182936667">"jelszó"</string>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index aeb39909ef9c..af803b4e7594 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Թեև մենք առանց գաղտնաբառերի ապագայի ճանապարհին ենք, դրանք դեռ հասանելի կլինեն անցաբառերի հետ մեկտեղ։"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Նշեք, թե որտեղ եք ուզում պահել ձեր <xliff:g id="CREATETYPES">%1$s</xliff:g>ը"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Ընտրեք գաղտնաբառերի կառավարիչ՝ ձեր տեղեկությունները պահելու և հաջորդ անգամ ավելի արագ մուտք գործելու համար"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Ստեղծե՞լ անցաբառ <xliff:g id="APPNAME">%1$s</xliff:g> հավելվածի համար"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Պահե՞լ «<xliff:g id="APPNAME">%1$s</xliff:g>» հավելվածի գաղտնաբառը"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Պահե՞լ «<xliff:g id="APPNAME">%1$s</xliff:g>» հավելվածի մուտքի տվյալները"</string>
<string name="passkey" msgid="632353688396759522">"անցաբառ"</string>
<string name="password" msgid="6738570945182936667">"գաղտնաբառ"</string>
diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml
index 8bc868617430..180c869a8430 100644
--- a/packages/CredentialManager/res/values-in/strings.xml
+++ b/packages/CredentialManager/res/values-in/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Sandi akan tetap tersedia bersama kunci sandi seiring perjalanan menuju era di mana sandi tidak diperlukan lagi."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Pilih tempat penyimpanan <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Pilih pengelola sandi untuk menyimpan info Anda dan login lebih cepat lain kali"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Buat kunci sandi untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Simpan sandi untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Simpan info login untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"kunci sandi"</string>
<string name="password" msgid="6738570945182936667">"sandi"</string>
diff --git a/packages/CredentialManager/res/values-is/strings.xml b/packages/CredentialManager/res/values-is/strings.xml
index e75746317889..332d15e4f888 100644
--- a/packages/CredentialManager/res/values-is/strings.xml
+++ b/packages/CredentialManager/res/values-is/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Við stefnum að framtíð án aðgangsorða en aðgangsorð verða áfram í boði samhliða aðgangslyklum."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Veldu hvar þú vilt vista <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Veldu aðgangsorðastjórnun til að vista upplýsingarnar og vera fljótari að skrá þig inn næst"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Viltu búa til aðgangslykil fyrir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Viltu vista aðgangsorð fyrir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Viltu vista innskráningarupplýsingar fyrir <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"aðgangslykill"</string>
<string name="password" msgid="6738570945182936667">"aðgangsorð"</string>
diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml
index 724137bfe962..5c8333693033 100644
--- a/packages/CredentialManager/res/values-it/strings.xml
+++ b/packages/CredentialManager/res/values-it/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Il futuro sarà senza password, ma per ora saranno ancora disponibili insieme alle passkey."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Scegli dove salvare le <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Seleziona un gestore delle password per salvare i tuoi dati e accedere più velocemente la prossima volta"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vuoi creare una passkey per <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Vuoi salvare la password di <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vuoi salvare i dati di accesso di <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml
index a3adedf89e82..55fb9f2912b2 100644
--- a/packages/CredentialManager/res/values-iw/strings.xml
+++ b/packages/CredentialManager/res/values-iw/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"אנחנו מתקדמים לעבר עתיד ללא סיסמאות, אבל עדיין אפשר יהיה להשתמש בסיסמאות וגם במפתחות גישה."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"בחירת המקום לשמירה של <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"אפשר לבחור באחד משירותי ניהול הסיסמאות כדי לשמור את הפרטים ולהיכנס לחשבון מהר יותר בפעם הבאה"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ליצור מפתח גישה ל-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"לשמור את הסיסמה של <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"לשמור את פרטי הכניסה של <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"מפתח גישה"</string>
<string name="password" msgid="6738570945182936667">"סיסמה"</string>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
index 2269f7ee2eee..1ffc7dbe931c 100644
--- a/packages/CredentialManager/res/values-ja/strings.xml
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"将来的にパスワードレスに移行するにあたり、パスワードもパスキーと並行して引き続きご利用いただけます。"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g>の保存先を選択"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"パスワード マネージャーを選択して情報を保存しておくと、次回からすばやくログインできます"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスキーを作成しますか?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスワードを保存しますか?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string>
<string name="passkey" msgid="632353688396759522">"パスキー"</string>
<string name="password" msgid="6738570945182936667">"パスワード"</string>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
index f58e4336b8fa..fdf079715181 100644
--- a/packages/CredentialManager/res/values-ka/strings.xml
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"უპაროლო მომავალში პაროლები კვლავ ხელმისაწვდომი იქნება, წვდომის გასაღებებთან ერთად."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"აირჩიეთ სად შეინახოთ თქვენი <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"აირჩიეთ პაროლების მმართველი თქვენი ინფორმაციის შესანახად, რომ მომავალში უფრო სწრაფად შეხვიდეთ."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"შექმნით წვდომის გასაღებს <xliff:g id="APPNAME">%1$s</xliff:g> აპისთვის?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"შეინახავთ <xliff:g id="APPNAME">%1$s</xliff:g> აპის პაროლს?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"შეინახავთ <xliff:g id="APPNAME">%1$s</xliff:g> აპში შესვლის ინფორმაციას?"</string>
<string name="passkey" msgid="632353688396759522">"წვდომის გასაღები"</string>
<string name="password" msgid="6738570945182936667">"პაროლი"</string>
diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml
index 79fe5ce096a6..1c1b186f88d8 100644
--- a/packages/CredentialManager/res/values-kk/strings.xml
+++ b/packages/CredentialManager/res/values-kk/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Құпия сөзсіз болашақ жақын болғанына қарамастан, келешекте құпия сөздерді кіру кілттерімен қатар қолдана беруге болады."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> қайда сақталатынын таңдаңыз"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Мәліметіңізді сақтап, келесіде жылдам кіру үшін құпия сөз менеджерін таңдаңыз."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> үшін кіру кілтін жасау керек пе?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> үшін құпия сөзді сақтау керек пе?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> үшін кіру мәліметін сақтау керек пе?"</string>
<string name="passkey" msgid="632353688396759522">"Кіру кілті"</string>
<string name="password" msgid="6738570945182936667">"құпия сөз"</string>
diff --git a/packages/CredentialManager/res/values-km/strings.xml b/packages/CredentialManager/res/values-km/strings.xml
index feba45e9b525..084087986f55 100644
--- a/packages/CredentialManager/res/values-km/strings.xml
+++ b/packages/CredentialManager/res/values-km/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"នៅពេលដែលយើងឈានទៅរកអនាគតដែលគ្មានពាក្យសម្ងាត់ ពាក្យសម្ងាត់នៅតែអាចប្រើបានរួមជាមួយកូដសម្ងាត់។"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"ជ្រើសរើសកន្លែង​ដែលត្រូវរក្សាទុក<xliff:g id="CREATETYPES">%1$s</xliff:g>របស់អ្នក"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"ជ្រើសរើស​កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ ដើម្បីរក្សាទុក​ព័ត៌មានរបស់អ្នក និងចូលគណនី​បានកាន់តែរហ័ស​នៅពេលលើកក្រោយ"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"បង្កើត​កូដសម្ងាត់​សម្រាប់ <xliff:g id="APPNAME">%1$s</xliff:g> ឬ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"រក្សាទុក​ពាក្យសម្ងាត់​សម្រាប់ <xliff:g id="APPNAME">%1$s</xliff:g> ឬ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"រក្សាទុក​ព័ត៌មានអំពី​ការចូលគណនីសម្រាប់ <xliff:g id="APPNAME">%1$s</xliff:g> ឬ?"</string>
<string name="passkey" msgid="632353688396759522">"កូដសម្ងាត់"</string>
<string name="password" msgid="6738570945182936667">"ពាក្យសម្ងាត់"</string>
diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml
index 17d49505386f..84549d1af0bc 100644
--- a/packages/CredentialManager/res/values-kn/strings.xml
+++ b/packages/CredentialManager/res/values-kn/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"ನಾವು ಪಾಸ್‌ವರ್ಡ್ ರಹಿತ ತಂತ್ರಜ್ಞಾನದ ಕಡೆಗೆ ಸಾಗುತ್ತಿರುವಾಗ, ಪಾಸ್‌ಕೀಗಳ ಜೊತೆಗೆ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಇನ್ನೂ ಲಭ್ಯವಿರುತ್ತವೆ."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"ನಿಮ್ಮ <xliff:g id="CREATETYPES">%1$s</xliff:g> ಎಲ್ಲಿ ಸೇವ್‌ ಆಗಬೇಕು ಎಂಬುದನ್ನು ಆರಿಸಿ"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಉಳಿಸಲು ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕವನ್ನು ಆಯ್ಕೆಮಾಡಿ ಹಾಗೂ ಮುಂದಿನ ಬಾರಿ ವೇಗವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್‌ಕೀ ಅನ್ನು ರಚಿಸುವುದೇ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್‌ವರ್ಡ್‌ ಉಳಿಸುವುದೇ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> ಗಾಗಿ ಸೈನ್-ಇನ್ ಮಾಹಿತಿಯನ್ನು ಉಳಿಸುವುದೇ?"</string>
<string name="passkey" msgid="632353688396759522">"ಪಾಸ್‌ಕೀ"</string>
<string name="password" msgid="6738570945182936667">"ಪಾಸ್‌ವರ್ಡ್"</string>
diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml
index c6bec2e5bff0..0c970dd3cae4 100644
--- a/packages/CredentialManager/res/values-ko/strings.xml
+++ b/packages/CredentialManager/res/values-ko/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"비밀번호 없는 미래로 나아가는 과정에서 비밀번호는 여전히 패스키와 함께 사용될 것입니다."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> 저장 위치 선택"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"정보를 저장해서 다음에 더 빠르게 로그인하려면 비밀번호 관리자를 선택하세요."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>의 패스키를 만드시겠습니까?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g>의 비밀번호를 저장하시겠습니까?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g>의 로그인 정보를 저장하시겠습니까?"</string>
<string name="passkey" msgid="632353688396759522">"패스키"</string>
<string name="password" msgid="6738570945182936667">"비밀번호"</string>
diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml
index effa3759d2d9..3937ff5afc31 100644
--- a/packages/CredentialManager/res/values-ky/strings.xml
+++ b/packages/CredentialManager/res/values-ky/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Сырсөзсүз келечекти көздөй баратсак да, аларды киргизүүчү ачкычтар менен бирге колдоно берүүгө болот."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> кайда сакталарын тандаңыз"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Маалыматыңызды сактоо жана кийинки жолу тезирээк кирүү үчүн сырсөздөрдү башкаргычты тандаңыз"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> колдонмосуна киргизүүчү ачкыч түзөсүзбү?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> үчүн сырсөз сакталсынбы?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> үчүн кирүү маалыматы сакталсынбы?"</string>
<string name="passkey" msgid="632353688396759522">"киргизүүчү ачкыч"</string>
<string name="password" msgid="6738570945182936667">"сырсөз"</string>
diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml
index d921f561c96b..79a31e8b1e8b 100644
--- a/packages/CredentialManager/res/values-lo/strings.xml
+++ b/packages/CredentialManager/res/values-lo/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"ໃນຂະນະທີ່ພວກເຮົາກ້າວໄປສູ່ອະນາຄົດທີ່ບໍ່ຕ້ອງໃຊ້ລະຫັດຜ່ານ, ລະຫັດຜ່ານຈະຍັງຄົງໃຊ້ໄດ້ຄວບຄູ່ໄປກັບກະແຈຜ່ານ."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"ເລືອກບ່ອນທີ່ຈະບັນທຶກ <xliff:g id="CREATETYPES">%1$s</xliff:g> ຂອງທ່ານ"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"ເລືອກຕົວຈັດການລະຫັດຜ່ານເພື່ອບັນທຶກຂໍ້ມູນຂອງທ່ານ ແລະ ເຂົ້າສູ່ລະບົບໄວຂຶ້ນໃນເທື່ອຕໍ່ໄປ"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ສ້າງກະແຈຜ່ານສຳລັບ <xliff:g id="APPNAME">%1$s</xliff:g> ບໍ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"ບັນທຶກລະຫັດຜ່ານສຳລັບ <xliff:g id="APPNAME">%1$s</xliff:g> ບໍ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"ບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບສຳລັບ <xliff:g id="APPNAME">%1$s</xliff:g> ບໍ?"</string>
<string name="passkey" msgid="632353688396759522">"ກະແຈຜ່ານ"</string>
<string name="password" msgid="6738570945182936667">"ລະຫັດຜ່ານ"</string>
diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml
index f4fc6d45866f..e6bcd0685dca 100644
--- a/packages/CredentialManager/res/values-lt/strings.xml
+++ b/packages/CredentialManager/res/values-lt/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Kol stengiamės padaryti, kad ateityje nereikėtų naudoti slaptažodžių, jie vis dar bus pasiekiami kartu su prieigos raktais."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Pasirinkite, kur išsaugoti „<xliff:g id="CREATETYPES">%1$s</xliff:g>“"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Pasirinkite slaptažodžių tvarkyklę, kurią naudodami galėsite išsaugoti informaciją ir kitą kartą prisijungti greičiau"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Sukurti „passkey“, skirtą „<xliff:g id="APPNAME">%1$s</xliff:g>“?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Išsaugoti „<xliff:g id="APPNAME">%1$s</xliff:g>“ slaptažodį?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Išsaugoti prisijungimo prie „<xliff:g id="APPNAME">%1$s</xliff:g>“ informaciją?"</string>
<string name="passkey" msgid="632353688396759522">"„passkey“"</string>
<string name="password" msgid="6738570945182936667">"slaptažodis"</string>
diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml
index e43b5a0b1dca..a62bf28223e8 100644
--- a/packages/CredentialManager/res/values-lv/strings.xml
+++ b/packages/CredentialManager/res/values-lv/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Lai arī pamazām notiek pāreja uz darbu bez parolēm, tās joprojām būs pieejamas līdzās piekļuves atslēgām."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Izvēlieties, kur saglabāt savas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Lai saglabātu informāciju un nākamreiz varētu pierakstīties ātrāk, atlasiet paroļu pārvaldnieku."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vai izveidot piekļuves atslēgu lietotnei <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Vai saglabāt paroli lietotnei <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vai saglabāt pierakstīšanās informāciju lietotnei <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"piekļuves atslēga"</string>
<string name="password" msgid="6738570945182936667">"parole"</string>
diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml
index 320664023832..b5d59969c7a4 100644
--- a/packages/CredentialManager/res/values-mk/strings.xml
+++ b/packages/CredentialManager/res/values-mk/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Како што се движиме кон иднина без лозинки, лозинките сепак ќе бидат достапни покрај криптографските клучеви."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Изберете каде да ги зачувате вашите <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Изберете управник со лозинки за да ги зачувате вашите податоци и да се најавите побрзо следниот пат"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Да се создаде криптографски клуч за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Дали да се зачува лозинката за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Да се зачуваат податоците за најавување за <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"криптографски клуч"</string>
<string name="password" msgid="6738570945182936667">"лозинка"</string>
diff --git a/packages/CredentialManager/res/values-ml/strings.xml b/packages/CredentialManager/res/values-ml/strings.xml
index 60d0af247c1c..fcbd12b20cb0 100644
--- a/packages/CredentialManager/res/values-ml/strings.xml
+++ b/packages/CredentialManager/res/values-ml/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"നമ്മൾ പാസ്‍വേഡ് രഹിത ഭാവിയിലേക്ക് ചുവടുവെച്ചുകൊണ്ടിരിക്കുകയാണ് എങ്കിലും, പാസ്‌കീകൾക്കൊപ്പം പാസ്‍വേഡുകൾ തുടർന്നും ലഭ്യമായിരിക്കും."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"നിങ്ങളുടെ <xliff:g id="CREATETYPES">%1$s</xliff:g> എവിടെയാണ് സംരക്ഷിക്കേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"നിങ്ങളുടെ വിവരങ്ങൾ സംരക്ഷിക്കാനും അടുത്ത തവണ വേഗത്തിൽ സൈൻ ഇൻ ചെയ്യാനും ഒരു പാസ്‌വേഡ് മാനേജർ തിരഞ്ഞെടുക്കുക"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> എന്നതിനായി പാസ്‌കീ സൃഷ്ടിക്കണോ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> എന്നതിനായി പാസ്‌വേഡ് സംരക്ഷിക്കണോ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> എന്നതിനായി സൈൻ ഇൻ വിവരങ്ങൾ സംരക്ഷിക്കണോ?"</string>
<string name="passkey" msgid="632353688396759522">"പാസ്‌കീ"</string>
<string name="password" msgid="6738570945182936667">"പാസ്‌വേഡ്"</string>
diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml
index 1050fc789756..b0d4ca6bea2d 100644
--- a/packages/CredentialManager/res/values-mn/strings.xml
+++ b/packages/CredentialManager/res/values-mn/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Бид нууц үггүй ирээдүй рүү урагшлахын хэрээр нууц үг нь нэвтрэх түлхүүрийн хамтаар боломжтой хэвээр байх болно."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g>-г хаана хадгалахаа сонгоно уу"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Мэдээллээ хадгалж, дараагийн удаа илүү хурдан нэвтрэхийн тулд нууц үгний менежерийг сонгоно уу"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g>-д passkey үүсгэх үү?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g>-н нууц үгийг хадгалах уу?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g>-н нэвтрэх мэдээллийг хадгалах уу?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"нууц үг"</string>
diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml
index 11fee3beb1de..5747afd88996 100644
--- a/packages/CredentialManager/res/values-mr/strings.xml
+++ b/packages/CredentialManager/res/values-mr/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"पासवर्ड न वापरणाऱ्या भविष्यात पुढे जाताना, पासवर्ड तरीही पासकीच्या बरोबरीने उपलब्ध असतील."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"तुमची <xliff:g id="CREATETYPES">%1$s</xliff:g> कुठे सेव्ह करायची ते निवडा"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"तुमची माहिती सेव्ह करण्यासाठी आणि पुढच्या वेळी जलद साइन इन करण्याकरिता Password Manager निवडा"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> साठी पासकी तयार करायची का?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> साठी पासवर्ड सेव्ह करायचा का?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> साठी साइन-इन माहिती सेव्ह करायची का?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml
index 8198707373d7..a6bc11d615f7 100644
--- a/packages/CredentialManager/res/values-ms/strings.xml
+++ b/packages/CredentialManager/res/values-ms/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Meskipun masa depan kita nanti tidak memerlukan kata laluan, kata laluan masih akan tersedia bersama dengan kunci laluan."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Pilih tempat untuk menyimpan <xliff:g id="CREATETYPES">%1$s</xliff:g> anda"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Pilih Password Manager untuk menyimpan maklumat anda dan log masuk lebih pantas pada kali seterusnya"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Cipta kunci laluan untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Simpan kata laluan untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Simpan maklumat log masuk untuk <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"kunci laluan"</string>
<string name="password" msgid="6738570945182936667">"kata laluan"</string>
diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml
index 02b80cf2e386..55eed78176b4 100644
--- a/packages/CredentialManager/res/values-my/strings.xml
+++ b/packages/CredentialManager/res/values-my/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"စကားဝှက်မသုံးခြင်း အနာဂတ်ဆီသို့ ရှေ့ဆက်ရာတွင် လျှို့ဝှက်ကီးများနှင့်အတူ စကားဝှက်များကို ဆက်လက်အသုံးပြုနိုင်ပါမည်။"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"သင်၏ <xliff:g id="CREATETYPES">%1$s</xliff:g> သိမ်းရန်နေရာ ရွေးခြင်း"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"သင့်အချက်အလက်သိမ်းပြီး နောက်တစ်ကြိမ်၌ ပိုမိုမြန်ဆန်စွာ လက်မှတ်ထိုးဝင်ရန် စကားဝှက်မန်နေဂျာကို ရွေးပါ"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> အတွက် လျှို့ဝှက်ကီးပြုလုပ်မလား။"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> အတွက် စကားဝှက်ကို သိမ်းမလား။"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> အတွက် လက်မှတ်ထိုးဝင်သည့်အချက်အလက်ကို သိမ်းမလား။"</string>
<string name="passkey" msgid="632353688396759522">"လျှို့ဝှက်ကီး"</string>
<string name="password" msgid="6738570945182936667">"စကားဝှက်"</string>
diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml
index 4ec8524c1daa..f7c5762c0598 100644
--- a/packages/CredentialManager/res/values-nb/strings.xml
+++ b/packages/CredentialManager/res/values-nb/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Vi går mot en fremtid uten passord, men passord fortsetter å være tilgjengelige ved siden av passnøkler."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Velg hvor du vil lagre <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Velg et verktøy for passordlagring for å lagre informasjonen din og logge på raskere neste gang"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vil du opprette en passnøkkel for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Vil du lagre passord for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vil du lagre påloggingsinformasjon for <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passnøkkel"</string>
<string name="password" msgid="6738570945182936667">"passord"</string>
diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml
index 28223ddc4403..bc3fc0ef1f09 100644
--- a/packages/CredentialManager/res/values-ne/strings.xml
+++ b/packages/CredentialManager/res/values-ne/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"हामी पासवर्डरहित भविष्यतर्फ बढ्दै गर्दा पासकीका साथसाथै पासवर्ड पनि उपलब्ध हुने छ।"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"तपाईं आफ्ना <xliff:g id="CREATETYPES">%1$s</xliff:g> कहाँ सेभ गर्न चाहनुहुन्छ भन्ने कुरा छनौट गर्नुहोस्"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"कुनै पासवर्ड म्यानेजरमा आफ्नो जानकारी सेभ गरी अर्को पटक अझ छिटो साइन इन गर्नुहोस्"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> को पासकी बनाउने हो?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> को पासवर्ड सेभ गर्ने हो?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> मा साइन गर्न प्रयोग गरिनु पर्ने जानकारी सेभ गर्ने हो?"</string>
<string name="passkey" msgid="632353688396759522">"पासकी"</string>
<string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml
index b82382c5b899..d4ce16b9d023 100644
--- a/packages/CredentialManager/res/values-nl/strings.xml
+++ b/packages/CredentialManager/res/values-nl/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"We zijn op weg naar een wachtwoordloze toekomst, maar naast toegangssleutels kun je nog steeds gebruikmaken van wachtwoorden."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Kiezen waar je je <xliff:g id="CREATETYPES">%1$s</xliff:g> wilt opslaan"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecteer een wachtwoordmanager om je informatie op te slaan en de volgende keer sneller in te loggen"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Toegangssleutel maken voor <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Wachtwoord opslaan voor <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Inloggegevens opslaan voor <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"Toegangssleutel"</string>
<string name="password" msgid="6738570945182936667">"wachtwoord"</string>
diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml
index 57853f01bf2f..4ca9c396c94d 100644
--- a/packages/CredentialManager/res/values-or/strings.xml
+++ b/packages/CredentialManager/res/values-or/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"ଆମେ ଏକ ପାସୱାର୍ଡବିହୀନ ଭବିଷ୍ୟତ ଆଡ଼କୁ ମୁଭ କରୁଥିବା ଯୋଗୁଁ ଏବେ ବି ପାସକୀଗୁଡ଼ିକ ସହିତ ପାସୱାର୍ଡ ଉପଲବ୍ଧ ହେବ।"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"ଆପଣଙ୍କ <xliff:g id="CREATETYPES">%1$s</xliff:g> କେଉଁଠାରେ ସେଭ କରିବେ ତାହା ବାଛନ୍ତୁ"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"ଆପଣଙ୍କ ସୂଚନା ସେଭ କରି ପରବର୍ତ୍ତୀ ସମୟରେ ଶୀଘ୍ର ସାଇନ ଇନ କରିବା ପାଇଁ ଏକ Password Manager ଚୟନ କରନ୍ତୁ"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> ପାଇଁ ପାସକୀ ତିଆରି କରିବେ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> ପାଇଁ ପାସୱାର୍ଡ ସେଭ କରିବେ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> ପାଇଁ ସାଇନ-ଇନର ସୂଚନା ସେଭ କରିବେ?"</string>
<string name="passkey" msgid="632353688396759522">"ପାସକୀ"</string>
<string name="password" msgid="6738570945182936667">"ପାସୱାର୍ଡ"</string>
diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml
index 25e1f0a59e6a..414a4cebf87c 100644
--- a/packages/CredentialManager/res/values-pa/strings.xml
+++ b/packages/CredentialManager/res/values-pa/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"ਹਾਲਾਂਕਿ, ਅਸੀਂ ਪਾਸਵਰਡ ਰਹਿਤ ਭਵਿੱਖ ਵੱਲ ਵਧ ਰਹੇ ਹਾਂ, ਪਰ ਪਾਸਕੀਆਂ ਦੇ ਨਾਲ ਪਾਸਵਰਡ ਹਾਲੇ ਵੀ ਉਪਲਬਧ ਹੋਣਗੇ।"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"ਚੁਣੋ ਕਿ ਆਪਣੀਆਂ <xliff:g id="CREATETYPES">%1$s</xliff:g> ਨੂੰ ਕਿੱਥੇ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"ਆਪਣੀ ਜਾਣਕਾਰੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਅਤੇ ਅਗਲੀ ਵਾਰ ਤੇਜ਼ੀ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ ਚੁਣੋ"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"ਕੀ <xliff:g id="APPNAME">%1$s</xliff:g> ਲਈ ਪਾਸਕੀ ਬਣਾਉਣੀ ਹੈ?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"ਕੀ <xliff:g id="APPNAME">%1$s</xliff:g> ਲਈ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"ਕੀ <xliff:g id="APPNAME">%1$s</xliff:g> ਲਈ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰਨੀ ਹੈ?"</string>
<string name="passkey" msgid="632353688396759522">"ਪਾਸਕੀ"</string>
<string name="password" msgid="6738570945182936667">"ਪਾਸਵਰਡ"</string>
diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml
index e581ff2f6358..91e227653eaf 100644
--- a/packages/CredentialManager/res/values-pl/strings.xml
+++ b/packages/CredentialManager/res/values-pl/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"W czasie przechodzenia na technologie niewymagające haseł możliwość stosowania haseł – niezależnie od kluczy dostępu – wciąż będzie dostępna."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Wybierz, gdzie zapisywać <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Wybierz menedżera haseł, aby zapisywać informacje i logować się szybciej"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Utworzyć klucz dla aplikacji <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Zapisać hasło do aplikacji <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Zapisać dane logowania do aplikacji <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"klucz"</string>
<string name="password" msgid="6738570945182936667">"hasło"</string>
diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml
index f6d03dfd51f3..105441f25267 100644
--- a/packages/CredentialManager/res/values-pt-rBR/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Estamos avançando em direção a um futuro sem senhas, mas elas ainda vão estar disponíveis junto às chaves de acesso."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Escolha onde salvar suas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecione um gerenciador de senhas para salvar suas informações e fazer login mais rapidamente na próxima vez"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Criar uma chave de acesso para o app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Salvar senha do app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Salvar informações de login do app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
<string name="password" msgid="6738570945182936667">"senha"</string>
diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml
index 0007fad11cc3..f7259d8f9711 100644
--- a/packages/CredentialManager/res/values-pt-rPT/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"À medida que avançamos para um futuro sem palavras-passe, as palavras-passe continuam disponíveis juntamente com as chaves de acesso."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Escolha onde guardar as suas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecione um gestor de palavras-passe para guardar as suas informações e iniciar sessão mais rapidamente da próxima vez"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Criar uma chave de acesso para a app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Guardar a palavra-passe da app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Guardar as informações de início de sessão da app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
<string name="password" msgid="6738570945182936667">"palavra-passe"</string>
diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml
index f6d03dfd51f3..105441f25267 100644
--- a/packages/CredentialManager/res/values-pt/strings.xml
+++ b/packages/CredentialManager/res/values-pt/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Estamos avançando em direção a um futuro sem senhas, mas elas ainda vão estar disponíveis junto às chaves de acesso."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Escolha onde salvar suas <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selecione um gerenciador de senhas para salvar suas informações e fazer login mais rapidamente na próxima vez"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Criar uma chave de acesso para o app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Salvar senha do app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Salvar informações de login do app <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
<string name="password" msgid="6738570945182936667">"senha"</string>
diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml
index 32d0056af72c..cfe61a96a382 100644
--- a/packages/CredentialManager/res/values-ro/strings.xml
+++ b/packages/CredentialManager/res/values-ro/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Ne îndreptăm spre un viitor fără parole, însă acestea sunt încă disponibile, alături de cheile de acces."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Alege unde dorești să salvezi <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Selectează un manager de parole pentru a salva informațiile și a te conecta mai rapid data viitoare"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Creezi o cheie de acces pentru <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Salvezi parola pentru <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Salvezi informațiile de conectare pentru <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"cheia de acces"</string>
<string name="password" msgid="6738570945182936667">"parolă"</string>
diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml
index a48b0f2ca1e2..ba7d34ae7bd6 100644
--- a/packages/CredentialManager/res/values-ru/strings.xml
+++ b/packages/CredentialManager/res/values-ru/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Хотя движение к будущему без паролей уже началось, их по-прежнему можно будет использовать наряду с ключами доступа."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Укажите, куда нужно сохранить <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Выберите менеджер паролей, чтобы сохранять учетные данные и быстро выполнять вход."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Создать ключ доступа для приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Сохранить пароль для приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Сохранить учетные данные для приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступа"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml
index d6113b371c9e..03ada97da904 100644
--- a/packages/CredentialManager/res/values-si/strings.xml
+++ b/packages/CredentialManager/res/values-si/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"අපි මුරපද රහිත අනාගතයක් කරා ගමන් කරන විට, මුරයතුරු සමග මුරපද තවමත් පවතී."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"ඔබේ <xliff:g id="CREATETYPES">%1$s</xliff:g> සුරැකිය යුතු ස්ථානය තෝරා ගන්න"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"ඔබේ තතු සුරැකීමට සහ මීළඟ වතාවේ වේගයෙන් පුරනය වීමට මුරපද කළමනාකරුවෙකු තෝරන්න"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> සඳහා මුරයතුර තනන්න ද?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> සඳහා මුරපදය සුරකින්න ද?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> සඳහා පුරනය වීමේ තතු සුරකින්න ද?"</string>
<string name="passkey" msgid="632353688396759522">"මුරයතුර"</string>
<string name="password" msgid="6738570945182936667">"මුරපදය"</string>
diff --git a/packages/CredentialManager/res/values-sk/strings.xml b/packages/CredentialManager/res/values-sk/strings.xml
index 62d8f190aa83..7198625818e1 100644
--- a/packages/CredentialManager/res/values-sk/strings.xml
+++ b/packages/CredentialManager/res/values-sk/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Blížime sa k budúcnosti bez hesiel, ale heslá budú popri prístupových kľúčoch stále k dispozícii."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Vyberte, kam sa majú ukladať <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Vyberte správcu hesiel, do ktorého sa budú ukladať vaše údaje, aby ste sa nabudúce mohli rýchlejšie prihlásiť"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Chcete vytvoriť prístupový kľúč pre aplikáciu <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Chcete uložiť heslo pre aplikáciu <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Chcete uložiť prihlasovacie údaje pre aplikáciu <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"prístupový kľúč"</string>
<string name="password" msgid="6738570945182936667">"heslo"</string>
diff --git a/packages/CredentialManager/res/values-sl/strings.xml b/packages/CredentialManager/res/values-sl/strings.xml
index 846d27d4219d..3ff85f0e41fb 100644
--- a/packages/CredentialManager/res/values-sl/strings.xml
+++ b/packages/CredentialManager/res/values-sl/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Na poti v prihodnost brez gesel bodo poleg ključev za dostop še vedno v uporabi tudi gesla."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Izbira mesta za shranjevanje <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Izberite upravitelja gesel za shranjevanje podatkov za prijavo, da se boste naslednjič lahko hitreje prijavili."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Želite ustvariti ključ za dostop do aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Želite shraniti geslo za aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Želite shraniti podatke za prijavo za aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ključ za dostop"</string>
<string name="password" msgid="6738570945182936667">"geslo"</string>
diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml
index 7678125e16a2..41f63915771f 100644
--- a/packages/CredentialManager/res/values-sq/strings.xml
+++ b/packages/CredentialManager/res/values-sq/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Teksa shkojmë drejt një të ardhmeje pa fjalëkalime, këto të fundit do të ofrohen ende së bashku me çelësat e kalimit."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Zgjidh se ku t\'i ruash <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Zgjidh një menaxher fjalëkalimesh për të ruajtur informacionet e tua dhe për t\'u identifikuar më shpejt herën tjetër"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Të krijohet çelësi i kalimit për <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Të ruhet fjalëkalimi për <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Të ruhen informacionet e identifikimit për <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"çelësin e kalimit"</string>
<string name="password" msgid="6738570945182936667">"fjalëkalimi"</string>
diff --git a/packages/CredentialManager/res/values-sr/strings.xml b/packages/CredentialManager/res/values-sr/strings.xml
index 8c71e71ae683..1a5567fe4c46 100644
--- a/packages/CredentialManager/res/values-sr/strings.xml
+++ b/packages/CredentialManager/res/values-sr/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Како се крећемо ка будућности без лозинки, лозинке ће и даље бити доступне уз приступне кодове."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Одаберите где ћете сачувати: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Изаберите менаџера лозинки да бисте сачували податке и брже се пријавили следећи пут"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Желите да направите приступни кôд за: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Желите да сачувате лозинку за: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Желите да сачувате податке за пријављивање за: <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"приступни кôд"</string>
<string name="password" msgid="6738570945182936667">"лозинка"</string>
diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml
index 9aa11653a5fa..8937b019ffc0 100644
--- a/packages/CredentialManager/res/values-sv/strings.xml
+++ b/packages/CredentialManager/res/values-sv/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Medan vi beger oss mot en lösenordslös framtid kommer lösenord fortfarande att vara tillgängliga utöver nycklar."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Välj var du vill spara <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Välj en lösenordshanterare för att spara dina uppgifter och logga in snabbare nästa gång"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Vill du skapa en nyckel för <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Vill du spara lösenordet för <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Vill du spara inloggningsuppgifterna för <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"nyckel"</string>
<string name="password" msgid="6738570945182936667">"lösenord"</string>
diff --git a/packages/CredentialManager/res/values-sw/strings.xml b/packages/CredentialManager/res/values-sw/strings.xml
index adf659ead166..051122f4ab71 100644
--- a/packages/CredentialManager/res/values-sw/strings.xml
+++ b/packages/CredentialManager/res/values-sw/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Tunavyoelekea katika enzi isiyo ya manenosiri, manenosiri yataendelea kupatikana pamoja na funguo za siri."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Chagua ambako unahifadhi <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Chagua kidhibiti cha manenosiri ili uhifadhi taarifa zako na uingie kwenye akaunti kwa urahisi wakati mwingine"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Ungependa kuunda ufunguo wa siri kwa ajili ya <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Ungependa kuhifadhi nenosiri kwa ajili ya <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Ungependa kuhifadhi maelezo ya kuingia katika akaunti kwa ajili ya <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ufunguo wa siri"</string>
<string name="password" msgid="6738570945182936667">"nenosiri"</string>
diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml
index c43776ea6d59..64d4a2458cd0 100644
--- a/packages/CredentialManager/res/values-ta/strings.xml
+++ b/packages/CredentialManager/res/values-ta/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"கடவுச்சொல்லற்ற எதிர்காலத்தை நோக்கி நாம் பயணிக்கிறோம். கடவுச்சாவிகளைப் பயன்படுத்தும் இதே வேளையில் கடவுச்சொற்களையும் பயன்படுத்த முடியும்."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"உங்கள் <xliff:g id="CREATETYPES">%1$s</xliff:g> எங்கே சேமிக்கப்பட வேண்டும் என்பதைத் தேர்வுசெய்யுங்கள்"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"உங்கள் தகவல்களைச் சேமித்து அடுத்த முறை விரைவாக உள்நுழைய ஒரு கடவுச்சொல் நிர்வாகியைத் தேர்வுசெய்யுங்கள்"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சாவியை உருவாக்கவா?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சொல்லைச் சேமிக்கவா?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸுக்கான உள்நுழைவு விவரங்களைச் சேமிக்கவா?"</string>
<string name="passkey" msgid="632353688396759522">"கடவுச்சாவி"</string>
<string name="password" msgid="6738570945182936667">"கடவுச்சொல்"</string>
diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml
index 980fb152c5ff..066f785282b1 100644
--- a/packages/CredentialManager/res/values-te/strings.xml
+++ b/packages/CredentialManager/res/values-te/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"మనం భవిష్యత్తులో పాస్‌వర్డ్ రహిత టెక్నాలజీని ఉపయోగించినా, పాస్‌కీలతో పాటు పాస్‌వర్డ్‌లు కూడా అందుబాటులో ఉంటాయి."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"మీ <xliff:g id="CREATETYPES">%1$s</xliff:g> ఎక్కడ సేవ్ చేయాలో ఎంచుకోండి"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"తర్వాతిసారి మరింత వేగంగా సైన్ ఇన్ చేసేందుకు వీలుగా మీ సమాచారాన్ని సేవ్ చేయడం కోసం ఒక పాస్‌వర్డ్ మేనేజర్‌ను ఎంచుకోండి"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> కోసం పాస్‌-కీని క్రియేట్ చేయాలా?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> కోసం పాస్‌వర్డ్‌ను సేవ్ చేయాలా?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> కోసం సైన్ ఇన్ సమాచారాన్ని సేవ్ చేయాలా?"</string>
<string name="passkey" msgid="632353688396759522">"పాస్-కీ"</string>
<string name="password" msgid="6738570945182936667">"పాస్‌వర్డ్"</string>
diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml
index e222d6b7a5b5..783d05769b20 100644
--- a/packages/CredentialManager/res/values-th/strings.xml
+++ b/packages/CredentialManager/res/values-th/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"ในขณะที่เราก้าวไปสู่อนาคตที่ไม่ต้องใช้รหัสผ่านนั้น รหัสผ่านจะยังคงใช้ได้อยู่ควบคู่ไปกับการเปลี่ยนไปใช้พาสคีย์"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"เลือกว่าต้องการบันทึก<xliff:g id="CREATETYPES">%1$s</xliff:g>ไว้ที่ใด"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"เลือกเครื่องมือจัดการรหัสผ่านเพื่อบันทึกข้อมูลและลงชื่อเข้าใช้เร็วขึ้นในครั้งถัดไป"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"สร้างพาสคีย์สำหรับ <xliff:g id="APPNAME">%1$s</xliff:g> ไหม"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"บันทึกรหัสผ่านสำหรับ <xliff:g id="APPNAME">%1$s</xliff:g> ไหม"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"บันทึกข้อมูลการลงชื่อเข้าใช้สำหรับ <xliff:g id="APPNAME">%1$s</xliff:g> ไหม"</string>
<string name="passkey" msgid="632353688396759522">"พาสคีย์"</string>
<string name="password" msgid="6738570945182936667">"รหัสผ่าน"</string>
diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml
index 5487df1be7d0..18283eacdba0 100644
--- a/packages/CredentialManager/res/values-tl/strings.xml
+++ b/packages/CredentialManager/res/values-tl/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Habang lumalayo tayo sa mga password, magiging available pa rin ang mga password kasama ng mga passkey."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Piliin kung saan mo ise-save ang iyong <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Pumili ng password manager para ma-save ang iyong impormasyon at makapag-sign in nang mas mabilis sa susunod na pagkakataon"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Gumawa ng passkey para sa <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"I-save ang password para sa <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"I-save ang impormasyon sa pag-sign in para sa <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"passkey"</string>
<string name="password" msgid="6738570945182936667">"password"</string>
diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml
index 5dda8aa316f0..8778797d5968 100644
--- a/packages/CredentialManager/res/values-tr/strings.xml
+++ b/packages/CredentialManager/res/values-tr/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Şifresiz bir geleceğe doğru ilerlerken şifreler, geçiş anahtarlarıyla birlikte kullanılmaya devam edecektir."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> kaydedileceği yeri seçin"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Bilgilerinizi kaydedip bir dahaki sefere daha hızlı oturum açmak için bir şifre yöneticisi seçin"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> için geçiş anahtarı oluşturulsun mu?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> için şifre kaydedilsin mi?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> için oturum açma bilgileri kaydedilsin mi?"</string>
<string name="passkey" msgid="632353688396759522">"Geçiş anahtarı"</string>
<string name="password" msgid="6738570945182936667">"Şifre"</string>
diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml
index 1cda5d4fc4dc..53de1b313c34 100644
--- a/packages/CredentialManager/res/values-uk/strings.xml
+++ b/packages/CredentialManager/res/values-uk/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"На шляху до безпарольного майбутнього паролі й надалі будуть використовуватися паралельно з ключами доступу."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Виберіть, де зберігати <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Виберіть менеджер паролів, щоб зберігати свої дані й надалі входити в облікові записи швидше"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Створити ключ доступу для додатка <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Зберегти пароль для додатка <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Зберегти дані для входу для додатка <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
<string name="password" msgid="6738570945182936667">"пароль"</string>
diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml
index 105045c6fb14..47e33fb5aeaf 100644
--- a/packages/CredentialManager/res/values-ur/strings.xml
+++ b/packages/CredentialManager/res/values-ur/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"چونکہ ہم بغیر پاس ورڈ والے مستقبل کی طرف جا رہے ہیں اس کے باوجود پاس ورڈز پاس کیز کے ساتھ ہی دستیاب ہوں گے۔"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"منتخب کریں کہ آپ کی <xliff:g id="CREATETYPES">%1$s</xliff:g> کو کہاں محفوظ کرنا ہے"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"اپنی معلومات کو محفوظ کرنے اور اگلی بار تیزی سے سائن ان کرنے کے لیے پاس ورڈ مینیجر منتخب کریں"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> کے لیے پاس کی تخلیق کریں؟"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> کے لیے پاس ورڈ کو محفوظ کریں؟"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> کے لیے سائن ان کی معلومات محفوظ کریں؟"</string>
<string name="passkey" msgid="632353688396759522">"پاس کی"</string>
<string name="password" msgid="6738570945182936667">"پاس ورڈ"</string>
diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml
index ec42f07d6a6b..6025cae04b6c 100644
--- a/packages/CredentialManager/res/values-uz/strings.xml
+++ b/packages/CredentialManager/res/values-uz/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Parolsiz kelajak sari harakatlanar ekanmiz, parollar kalitlar bilan birga ishlatilishda davom etadi."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Bu <xliff:g id="CREATETYPES">%1$s</xliff:g> qayerga saqlanishini tanlang"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Maʼlumotlaringizni saqlash va keyingi safar tez kirish uchun parollar menejerini tanlang"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> uchun kod yaratilsinmi?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> uchun parol saqlansinmi?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> uchun kirish maʼlumoti saqlansinmi?"</string>
<string name="passkey" msgid="632353688396759522">"kalit"</string>
<string name="password" msgid="6738570945182936667">"parol"</string>
diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml
index dbee6589b5fd..141103630ee3 100644
--- a/packages/CredentialManager/res/values-vi/strings.xml
+++ b/packages/CredentialManager/res/values-vi/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Trong quá trình chúng tôi hướng đến tương lai không dùng mật khẩu, bạn vẫn sẽ dùng được mật khẩu cùng với khoá truy cập."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Chọn vị trí lưu <xliff:g id="CREATETYPES">%1$s</xliff:g> của bạn"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Hãy chọn một trình quản lý mật khẩu để lưu thông tin của bạn và đăng nhập nhanh hơn vào lần tới"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Tạo khoá đăng nhập cho <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Lưu mật khẩu cho <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Lưu thông tin đăng nhập cho <xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"khoá đăng nhập"</string>
<string name="password" msgid="6738570945182936667">"mật khẩu"</string>
diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml
index c82f2f8bb49d..dcc226986d23 100644
--- a/packages/CredentialManager/res/values-zh-rCN/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"在我们向无密码未来迈进的过程中,密码仍会与通行密钥并行使用。"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"选择保存<xliff:g id="CREATETYPES">%1$s</xliff:g>的位置"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"请选择一款密码管理工具来保存您的信息,以便下次更快地登录"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"要为“<xliff:g id="APPNAME">%1$s</xliff:g>”创建通行密钥吗?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"要保存“<xliff:g id="APPNAME">%1$s</xliff:g>”的密码吗?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"要保存“<xliff:g id="APPNAME">%1$s</xliff:g>”的登录信息吗?"</string>
<string name="passkey" msgid="632353688396759522">"通行密钥"</string>
<string name="password" msgid="6738570945182936667">"密码"</string>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
index 1d3e5aabd797..5e893f6d8d4c 100644
--- a/packages/CredentialManager/res/values-zh-rHK/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"我們將會改用無密碼技術,而密碼仍可與密鑰並行使用。"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"選擇儲存<xliff:g id="CREATETYPES">%1$s</xliff:g>的位置"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"選取密碼管理工具即可儲存自己的資料,縮短下次登入的時間"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"要為「<xliff:g id="APPNAME">%1$s</xliff:g>」建立密鑰嗎?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"要儲存「<xliff:g id="APPNAME">%1$s</xliff:g>」的密碼嗎?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"要儲存「<xliff:g id="APPNAME">%1$s</xliff:g>」的登入資料嗎?"</string>
<string name="passkey" msgid="632353688396759522">"密鑰"</string>
<string name="password" msgid="6738570945182936667">"密碼"</string>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
index 184505a05ba6..1e1dca491961 100644
--- a/packages/CredentialManager/res/values-zh-rTW/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"我們日後將改採無密碼技術,密碼仍可與密碼金鑰並行使用。"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"選擇要將<xliff:g id="CREATETYPES">%1$s</xliff:g>存在哪裡"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"選取密碼管理工具並儲存資訊,下次就能更快登入"</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"要為「<xliff:g id="APPNAME">%1$s</xliff:g>」建立密碼金鑰嗎?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"要儲存「<xliff:g id="APPNAME">%1$s</xliff:g>」的密碼嗎?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"要儲存「<xliff:g id="APPNAME">%1$s</xliff:g>」的登入資訊嗎?"</string>
<string name="passkey" msgid="632353688396759522">"密碼金鑰"</string>
<string name="password" msgid="6738570945182936667">"密碼"</string>
diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml
index 8feeb17d7e3e..72a1e8f43a0a 100644
--- a/packages/CredentialManager/res/values-zu/strings.xml
+++ b/packages/CredentialManager/res/values-zu/strings.xml
@@ -39,8 +39,10 @@
<string name="seamless_transition_detail" msgid="4475509237171739843">"Njengoba sibhekela kwikusasa elingenaphasiwedi, amagama ayimfihlo asazotholakala eceleni kokhiye bokudlula."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Khetha lapho ongagcina khona i-<xliff:g id="CREATETYPES">%1$s</xliff:g> yakho"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Khetha isiphathi sephasiwedi ukuze ulondoloze ulwazi lwakho futhi ungene ngemvume ngokushesha ngesikhathi esizayo."</string>
- <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"Sungula ukhiye wokudlula we-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
- <string name="choose_create_option_password_title" msgid="7097275038523578687">"Londolozela amaphasiwedi ye-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for choose_create_option_passkey_title (7980430650778623135) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (6238446571944651980) -->
+ <skip />
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Londoloza ulwazi lokungena lwe-<xliff:g id="APPNAME">%1$s</xliff:g>?"</string>
<string name="passkey" msgid="632353688396759522">"ukhiye wokudlula"</string>
<string name="password" msgid="6738570945182936667">"iphasiwedi"</string>
diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml
index 350920b23c69..82dee5c22736 100644
--- a/packages/CredentialManager/res/values/dimens.xml
+++ b/packages/CredentialManager/res/values/dimens.xml
@@ -26,7 +26,7 @@
<dimen name="autofill_dropdown_textview_min_width">112dp</dimen>
<dimen name="autofill_dropdown_textview_max_width">230dp</dimen>
<dimen name="dropdown_layout_horizontal_margin">24dp</dimen>
- <integer name="autofill_max_visible_datasets">5</integer>
+ <integer name="autofill_max_visible_datasets">4</integer>
<dimen name="dropdown_touch_target_min_height">48dp</dimen>
<dimen name="horizontal_chip_padding">8dp</dimen>
<dimen name="vertical_chip_padding">6dp</dimen>
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 82b47a964204..527701c06bc6 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -63,9 +63,9 @@
<!-- This appears as the description body of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
<string name="choose_provider_body">Select a password manager to save your info and sign in faster next time</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is passkey. [CHAR LIMIT=200] -->
- <string name="choose_create_option_passkey_title">Create passkey for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+ <string name="choose_create_option_passkey_title">Create passkey to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is password. [CHAR LIMIT=200] -->
- <string name="choose_create_option_password_title">Save password for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+ <string name="choose_create_option_password_title">Save password to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] -->
<string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] -->
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
index 39d3f42e8bf4..892eabf14191 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.util.Log
import androidx.activity.result.IntentSenderRequest
+import androidx.credentials.PasswordCredential
import androidx.credentials.PublicKeyCredential
import androidx.credentials.provider.Action
import androidx.credentials.provider.AuthenticationAction
@@ -47,8 +48,9 @@ import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.EntryInfo
-fun CredentialEntryInfo.getIntentSenderRequest(
+fun EntryInfo.getIntentSenderRequest(
isAutoSelected: Boolean = false
): IntentSenderRequest? {
val entryIntent = fillInIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
@@ -124,6 +126,7 @@ private fun getCredentialOptionInfoList(
pendingIntent = credentialEntry.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
credentialType = CredentialType.PASSWORD,
+ rawCredentialType = PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
@@ -132,6 +135,10 @@ private fun getCredentialOptionInfoList(
lastUsedTimeMillis = credentialEntry.lastUsedTime,
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
credentialEntry.isAutoSelectAllowedFromOption,
+ entryGroupId = credentialEntry.entryGroupId.toString(),
+ isDefaultIconPreferredAsSingleProvider =
+ credentialEntry.isDefaultIconPreferredAsSingleProvider,
+ affiliatedDomain = credentialEntry.affiliatedDomain?.toString(),
)
)
}
@@ -145,6 +152,7 @@ private fun getCredentialOptionInfoList(
pendingIntent = credentialEntry.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
credentialType = CredentialType.PASSKEY,
+ rawCredentialType = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
@@ -155,6 +163,10 @@ private fun getCredentialOptionInfoList(
lastUsedTimeMillis = credentialEntry.lastUsedTime,
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
credentialEntry.isAutoSelectAllowedFromOption,
+ entryGroupId = credentialEntry.entryGroupId.toString(),
+ isDefaultIconPreferredAsSingleProvider =
+ credentialEntry.isDefaultIconPreferredAsSingleProvider,
+ affiliatedDomain = credentialEntry.affiliatedDomain?.toString(),
)
)
}
@@ -168,6 +180,7 @@ private fun getCredentialOptionInfoList(
pendingIntent = credentialEntry.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
credentialType = CredentialType.UNKNOWN,
+ rawCredentialType = credentialEntry.type,
credentialTypeDisplayName =
credentialEntry.typeDisplayName?.toString().orEmpty(),
userName = credentialEntry.title.toString(),
@@ -177,6 +190,10 @@ private fun getCredentialOptionInfoList(
lastUsedTimeMillis = credentialEntry.lastUsedTime,
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
credentialEntry.isAutoSelectAllowedFromOption,
+ entryGroupId = credentialEntry.entryGroupId.toString(),
+ isDefaultIconPreferredAsSingleProvider =
+ credentialEntry.isDefaultIconPreferredAsSingleProvider,
+ affiliatedDomain = credentialEntry.affiliatedDomain?.toString(),
)
)
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
index 9725881e1c97..a657e97de3cc 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
@@ -31,6 +31,11 @@ class CredentialEntryInfo(
fillInIntent: Intent?,
/** Type of this credential used for sorting. Not localized so must not be directly displayed. */
val credentialType: CredentialType,
+ /**
+ * String type value of this credential used for sorting. Not localized so must not be directly
+ * displayed.
+ */
+ val rawCredentialType: String,
/** Localized type value of this credential used for display purpose. */
val credentialTypeDisplayName: String,
val providerDisplayName: String,
@@ -40,6 +45,10 @@ class CredentialEntryInfo(
val shouldTintIcon: Boolean,
val lastUsedTimeMillis: Instant?,
val isAutoSelectable: Boolean,
+ val entryGroupId: String, // Used for deduplication, and displayed as the grouping title
+ // "For <value-of-entryGroupId>" on the more-option screen.
+ val isDefaultIconPreferredAsSingleProvider: Boolean,
+ val affiliatedDomain: String?,
) : EntryInfo(
providerId,
entryKey,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index ccf401da5a4c..6a1998a5e24e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -20,6 +20,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
+import android.credentials.GetCredentialRequest
import android.credentials.selection.CreateCredentialProviderData
import android.credentials.selection.DisabledProviderData
import android.credentials.selection.Entry
@@ -44,6 +45,9 @@ import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.PasswordCredential
+import androidx.credentials.PriorityHints
+import androidx.credentials.PublicKeyCredential
import androidx.credentials.provider.CreateEntry
import androidx.credentials.provider.RemoteEntry
import org.json.JSONObject
@@ -162,6 +166,25 @@ private fun getPackageInfo(
/** Utility functions for converting CredentialManager data structures to or from UI formats. */
class GetFlowUtils {
companion object {
+ fun extractTypePriorityMap(request: GetCredentialRequest): Map<String, Int> {
+ val typePriorityMap = mutableMapOf<String, Int>()
+ request.credentialOptions.forEach {option ->
+ // TODO(b/280085288) - use jetpack conversion method when exposed, rather than
+ // parsing from the raw Bundle
+ val priority = option.candidateQueryData.getInt(
+ "androidx.credentials.BUNDLE_KEY_TYPE_PRIORITY_VALUE",
+ when (option.type) {
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL ->
+ PriorityHints.PRIORITY_PASSWORD_OR_SIMILAR
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> 100
+ else -> PriorityHints.PRIORITY_DEFAULT
+ }
+ )
+ typePriorityMap[option.type] = priority
+ }
+ return typePriorityMap
+ }
+
// Returns the list (potentially empty) of enabled provider.
fun toProviderList(
providerDataList: List<GetCredentialProviderData>,
@@ -193,6 +216,9 @@ class GetFlowUtils {
null
}
}
+
+ val typePriorityMap = extractTypePriorityMap(getCredentialRequest)
+
return com.android.credentialmanager.getflow.RequestDisplayInfo(
appName = originName?.ifEmpty { null }
?: getAppLabel(context.packageManager, requestInfo.packageName)
@@ -203,6 +229,7 @@ class GetFlowUtils {
// exposed.
"androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI"),
preferTopBrandingContent = preferTopBrandingContent,
+ typePriorityMap = typePriorityMap,
)
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 13260231038d..3363ac025f12 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -29,11 +29,11 @@ import android.credentials.CredentialOption
import android.credentials.selection.Entry
import android.credentials.selection.GetCredentialProviderData
import android.credentials.selection.ProviderData
+import android.graphics.BlendMode
import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
-import android.provider.Settings
import android.service.autofill.AutofillService
import android.service.autofill.Dataset
import android.service.autofill.Field
@@ -140,7 +140,7 @@ class CredentialAutofillService : AutofillService() {
override fun onResult(result: GetCandidateCredentialsResponse) {
Log.i(TAG, "getCandidateCredentials onResult")
val fillResponse = convertToFillResponse(result, request,
- responseClientState)
+ responseClientState, GetFlowUtils.extractTypePriorityMap(getCredRequest))
if (fillResponse != null) {
callback.onSuccess(fillResponse)
} else {
@@ -197,7 +197,8 @@ class CredentialAutofillService : AutofillService() {
private fun convertToFillResponse(
getCredResponse: GetCandidateCredentialsResponse,
filLRequest: FillRequest,
- responseClientState: Bundle
+ responseClientState: Bundle,
+ typePriorityMap: Map<String, Int>,
): FillResponse? {
val candidateProviders = getCredResponse.candidateProviderDataList
if (candidateProviders.isEmpty()) {
@@ -213,7 +214,7 @@ class CredentialAutofillService : AutofillService() {
autofillIdToProvidersMap.forEach { (autofillId, providers) ->
validFillResponse = processProvidersForAutofillId(
filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder,
- getCredResponse.intent)
+ getCredResponse.intent, typePriorityMap)
.or(validFillResponse)
}
if (!validFillResponse) {
@@ -229,7 +230,8 @@ class CredentialAutofillService : AutofillService() {
providerDataList: ArrayList<GetCredentialProviderData>,
entryIconMap: Map<String, Icon>,
fillResponseBuilder: FillResponse.Builder,
- bottomSheetIntent: Intent
+ bottomSheetIntent: Intent,
+ typePriorityMap: Map<String, Int>,
): Boolean {
val providerList = GetFlowUtils.toProviderList(
providerDataList,
@@ -237,23 +239,15 @@ class CredentialAutofillService : AutofillService() {
if (providerList.isEmpty()) {
return false
}
- var totalEntryCount = 0
- providerList.forEach { provider ->
- totalEntryCount += provider.credentialEntryList.size
- }
- val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList)
+ val providerDisplayInfo: ProviderDisplayInfo =
+ toProviderDisplayInfo(providerList, typePriorityMap)
+ var totalEntryCount = providerDisplayInfo.sortedUserNameToCredentialEntryList.size
val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
- val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
val inlinePresentationSpecsCount = inlinePresentationSpecs?.size ?: 0
- val maxDropdownDisplayLimit = this.resources.getInteger(
+ val maxDatasetDisplayLimit = this.resources.getInteger(
com.android.credentialmanager.R.integer.autofill_max_visible_datasets)
- var maxInlineItemCount = totalEntryCount
- maxInlineItemCount = maxInlineItemCount.coerceAtMost(inlineMaxSuggestedCount)
- val lastDropdownDatasetIndex = Settings.Global.getInt(this.contentResolver,
- Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
- (maxDropdownDisplayLimit - 1)).coerceAtMost(totalEntryCount)
-
+ .coerceAtMost(totalEntryCount)
var i = 0
var datasetAdded = false
@@ -278,7 +272,7 @@ class CredentialAutofillService : AutofillService() {
Log.e(TAG, "PendingIntent was missing from the entry.")
return@usernameLoop
}
- if (i >= maxInlineItemCount && i >= lastDropdownDatasetIndex) {
+ if (i >= maxDatasetDisplayLimit) {
return@usernameLoop
}
val icon: Icon = if (primaryEntry.icon == null) {
@@ -291,7 +285,7 @@ class CredentialAutofillService : AutofillService() {
}
// Create inline presentation
var inlinePresentation: InlinePresentation? = null
- if (inlinePresentationSpecs != null && i < maxInlineItemCount) {
+ if (inlinePresentationSpecs != null && i < maxDatasetDisplayLimit) {
val spec: InlinePresentationSpec? = if (i < inlinePresentationSpecsCount) {
inlinePresentationSpecs[i]
} else {
@@ -305,7 +299,7 @@ class CredentialAutofillService : AutofillService() {
}
}
var dropdownPresentation: RemoteViews? = null
- if (i < lastDropdownDatasetIndex) {
+ if (i < maxDatasetDisplayLimit) {
dropdownPresentation = RemoteViewsFactory
.createDropdownPresentation(this, icon, primaryEntry)
}
@@ -331,17 +325,15 @@ class CredentialAutofillService : AutofillService() {
.build())
datasetAdded = true
i++
-
- if (i == lastDropdownDatasetIndex) {
- addDropdownMoreOptionsPresentation(bottomSheetIntent, autofillId,
- fillResponseBuilder)
- }
}
val pinnedSpec = getLastInlinePresentationSpec(inlinePresentationSpecs,
inlinePresentationSpecsCount)
- if (datasetAdded && pinnedSpec != null) {
- addPinnedInlineSuggestion(pinnedSpec, autofillId,
- fillResponseBuilder, bottomSheetIntent)
+ if (datasetAdded) {
+ addDropdownMoreOptionsPresentation(bottomSheetIntent, autofillId, fillResponseBuilder)
+ if (pinnedSpec != null) {
+ addPinnedInlineSuggestion(pinnedSpec, autofillId,
+ fillResponseBuilder, bottomSheetIntent)
+ }
}
return datasetAdded
}
@@ -362,6 +354,7 @@ class CredentialAutofillService : AutofillService() {
val sliceBuilder = InlineSuggestionUi
.newContentBuilder(pendingIntent)
.setTitle(displayName)
+ icon.setTintBlendMode(BlendMode.DST)
sliceBuilder.setStartIcon(icon)
if (primaryEntry.credentialType ==
CredentialType.PASSKEY && duplicateDisplayNameForPasskeys[displayName] == true) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index 8ff17e0d333a..965ee860672e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -78,6 +78,8 @@ fun Entry(
isLockedAuthEntry: Boolean = false,
enforceOneLine: Boolean = false,
onTextLayout: (TextLayoutResult) -> Unit = {},
+ /** Get flow only, if present, where be drawn as a line above the headline. */
+ affiliatedDomainText: String? = null,
) {
val iconPadding = Modifier.wrapContentSize().padding(
// Horizontal padding should be 16dp, but the suggestion chip itself
@@ -102,6 +104,13 @@ fun Entry(
) {
// Apply weight so that the trailing icon can always show.
Column(modifier = Modifier.wrapContentHeight().fillMaxWidth().weight(1f)) {
+ if (!affiliatedDomainText.isNullOrBlank()) {
+ BodySmallText(
+ text = affiliatedDomainText,
+ enforceOneLine = enforceOneLine,
+ onTextLayout = onTextLayout,
+ )
+ }
SmallTitleText(
text = entryHeadlineText,
enforceOneLine = enforceOneLine,
@@ -143,14 +152,14 @@ fun Entry(
},
)
}
- } else if (entrySecondLineText != null) {
+ } else if (!entrySecondLineText.isNullOrBlank()) {
BodySmallText(
text = entrySecondLineText,
enforceOneLine = enforceOneLine,
onTextLayout = onTextLayout,
)
}
- if (entryThirdLineText != null) {
+ if (!entryThirdLineText.isNullOrBlank()) {
BodySmallText(
text = entryThirdLineText,
enforceOneLine = enforceOneLine,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt
index 3ebdd204b640..ff421bc47511 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt
@@ -21,63 +21,20 @@ package com.android.credentialmanager.common.ui
import android.content.Context
import android.util.Size
import android.widget.inline.InlinePresentationSpec
-import androidx.autofill.inline.common.TextViewStyle
-import androidx.autofill.inline.common.ViewStyle
-import androidx.autofill.inline.UiVersions
-import androidx.autofill.inline.UiVersions.Style
-import androidx.autofill.inline.v1.InlineSuggestionUi
-import androidx.core.content.ContextCompat
-import android.util.TypedValue
-import android.graphics.Typeface
-
class InlinePresentationsFactory {
companion object {
- private const val googleSansMediumFontFamily = "google-sans-medium"
- private const val googleSansTextFontFamily = "google-sans-text"
- // There is no min width required for now but this is needed for the spec builder
- private const val minInlineWidth = 5000
+ // There is no max width required for now but this is needed for the spec builder
+ private const val maxInlineWidth = 5000
fun modifyInlinePresentationSpec(context: Context,
originalSpec: InlinePresentationSpec): InlinePresentationSpec {
return InlinePresentationSpec.Builder(Size(originalSpec.minSize.width, originalSpec
.minSize.height),
- Size(minInlineWidth, originalSpec
+ Size(maxInlineWidth, originalSpec
.maxSize.height))
- .setStyle(UiVersions.newStylesBuilder().addStyle(getStyle(context)).build())
- .build()
- }
-
-
- fun getStyle(context: Context): Style {
- val textColorPrimary = ContextCompat.getColor(context,
- com.android.credentialmanager.R.color.text_primary)
- val textColorSecondary = ContextCompat.getColor(context,
- com.android.credentialmanager.R.color.text_secondary)
- val textColorBackground = ContextCompat.getColor(context,
- com.android.credentialmanager.R.color.inline_background)
- val chipHorizontalPadding = context.resources.getDimensionPixelSize(com.android
- .credentialmanager.R.dimen.horizontal_chip_padding)
- val chipVerticalPadding = context.resources.getDimensionPixelSize(com.android
- .credentialmanager.R.dimen.vertical_chip_padding)
- return InlineSuggestionUi.newStyleBuilder()
- .setChipStyle(
- ViewStyle.Builder().setPadding(chipHorizontalPadding,
- chipVerticalPadding,
- chipHorizontalPadding, chipVerticalPadding).build()
- )
- .setTitleStyle(
- TextViewStyle.Builder().setTextColor(textColorPrimary).setTextSize
- (TypedValue.COMPLEX_UNIT_DIP, 14F)
- .setTypeface(googleSansMediumFontFamily,
- Typeface.NORMAL).setBackgroundColor(textColorBackground)
- .build()
- )
- .setSubtitleStyle(TextViewStyle.Builder().setTextColor(textColorSecondary)
- .setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12F).setTypeface
- (googleSansTextFontFamily, Typeface.NORMAL).setBackgroundColor
- (textColorBackground).build())
+ .setStyle(originalSpec.getStyle())
.build()
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
index 02afc547e3f8..7966a86903f3 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
@@ -34,9 +34,9 @@ class RemoteViewsFactory {
private const val passwordCharacterLength = 15
fun createDropdownPresentation(
- context: Context,
- icon: Icon,
- credentialEntryInfo: CredentialEntryInfo
+ context: Context,
+ icon: Icon,
+ credentialEntryInfo: CredentialEntryInfo
): RemoteViews {
var layoutId: Int = com.android.credentialmanager.R.layout
.credman_dropdown_presentation_layout
@@ -45,41 +45,37 @@ class RemoteViewsFactory {
return remoteViews
}
setRemoteViewsPaddings(remoteViews, context, /* primaryTextBottomPadding=*/0)
- if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) {
- val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName
- remoteViews.setTextViewText(android.R.id.text1, displayName)
- val secondaryText = if (credentialEntryInfo.displayName != null)
+ val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName
+ remoteViews.setTextViewText(android.R.id.text1, displayName)
+ val secondaryText =
+ if (credentialEntryInfo.displayName != null
+ && (credentialEntryInfo.displayName != credentialEntryInfo.userName))
(credentialEntryInfo.userName + " " + bulletPoint + " "
+ credentialEntryInfo.credentialTypeDisplayName
+ " " + bulletPoint + " " + credentialEntryInfo.providerDisplayName)
else (credentialEntryInfo.credentialTypeDisplayName + " " + bulletPoint + " "
+ credentialEntryInfo.providerDisplayName)
- remoteViews.setTextViewText(android.R.id.text2, secondaryText)
- } else {
- remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName)
- remoteViews.setTextViewText(android.R.id.text2,
- bulletPoint.repeat(passwordCharacterLength))
- }
+ remoteViews.setTextViewText(android.R.id.text2, secondaryText)
val textColorPrimary = ContextCompat.getColor(context,
- com.android.credentialmanager.R.color.text_primary)
+ com.android.credentialmanager.R.color.text_primary)
remoteViews.setTextColor(android.R.id.text1, textColorPrimary)
val textColorSecondary = ContextCompat.getColor(context, com.android
.credentialmanager.R.color.text_secondary)
remoteViews.setTextColor(android.R.id.text2, textColorSecondary)
remoteViews.setImageViewIcon(android.R.id.icon1, icon);
remoteViews.setBoolean(
- android.R.id.icon1, setAdjustViewBoundsMethodName, true);
+ android.R.id.icon1, setAdjustViewBoundsMethodName, true);
remoteViews.setInt(
- android.R.id.icon1,
- setMaxHeightMethodName,
- context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_icon_size));
+ android.R.id.icon1,
+ setMaxHeightMethodName,
+ context.resources.getDimensionPixelSize(
+ com.android.credentialmanager.R.dimen.autofill_icon_size));
remoteViews.setContentDescription(android.R.id.icon1, credentialEntryInfo
.providerDisplayName);
val drawableId =
- com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one
+ com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one
remoteViews.setInt(
- android.R.id.content, setBackgroundResourceMethodName, drawableId);
+ android.R.id.content, setBackgroundResourceMethodName, drawableId);
return remoteViews
}
@@ -89,68 +85,68 @@ class RemoteViewsFactory {
val remoteViews = RemoteViews(context.packageName, layoutId)
setRemoteViewsPaddings(remoteViews, context)
remoteViews.setTextViewText(android.R.id.text1, ContextCompat.getString(context,
- com.android.credentialmanager
- .R.string.dropdown_presentation_more_sign_in_options_text))
+ com.android.credentialmanager
+ .R.string.dropdown_presentation_more_sign_in_options_text))
val textColorPrimary = ContextCompat.getColor(context,
- com.android.credentialmanager.R.color.text_primary)
+ com.android.credentialmanager.R.color.text_primary)
remoteViews.setTextColor(android.R.id.text1, textColorPrimary)
val icon = Icon.createWithResource(context, com
.android.credentialmanager.R.drawable.more_horiz_24px)
icon.setTint(ContextCompat.getColor(context,
- com.android.credentialmanager.R.color.sign_in_options_icon_color))
+ com.android.credentialmanager.R.color.sign_in_options_icon_color))
remoteViews.setImageViewIcon(android.R.id.icon1, icon)
remoteViews.setBoolean(
- android.R.id.icon1, setAdjustViewBoundsMethodName, true);
+ android.R.id.icon1, setAdjustViewBoundsMethodName, true);
remoteViews.setInt(
- android.R.id.icon1,
- setMaxHeightMethodName,
- context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_icon_size));
+ android.R.id.icon1,
+ setMaxHeightMethodName,
+ context.resources.getDimensionPixelSize(
+ com.android.credentialmanager.R.dimen.autofill_icon_size));
val drawableId =
- com.android.credentialmanager.R.drawable.more_options_list_item
+ com.android.credentialmanager.R.drawable.more_options_list_item
remoteViews.setInt(
- android.R.id.content, setBackgroundResourceMethodName, drawableId);
+ android.R.id.content, setBackgroundResourceMethodName, drawableId);
return remoteViews
}
private fun setRemoteViewsPaddings(
- remoteViews: RemoteViews, context: Context) {
+ remoteViews: RemoteViews, context: Context) {
val bottomPadding = context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_view_bottom_padding)
+ com.android.credentialmanager.R.dimen.autofill_view_bottom_padding)
setRemoteViewsPaddings(remoteViews, context, bottomPadding)
}
private fun setRemoteViewsPaddings(
- remoteViews: RemoteViews, context: Context, primaryTextBottomPadding: Int) {
+ remoteViews: RemoteViews, context: Context, primaryTextBottomPadding: Int) {
val leftPadding = context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_view_left_padding)
+ com.android.credentialmanager.R.dimen.autofill_view_left_padding)
val iconToTextPadding = context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding)
+ com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding)
val rightPadding = context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_view_right_padding)
+ com.android.credentialmanager.R.dimen.autofill_view_right_padding)
val topPadding = context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_view_top_padding)
+ com.android.credentialmanager.R.dimen.autofill_view_top_padding)
val bottomPadding = context.resources.getDimensionPixelSize(
- com.android.credentialmanager.R.dimen.autofill_view_bottom_padding)
+ com.android.credentialmanager.R.dimen.autofill_view_bottom_padding)
remoteViews.setViewPadding(
- android.R.id.icon1,
- leftPadding,
- /* top=*/0,
- /* right=*/0,
- /* bottom=*/0)
+ android.R.id.icon1,
+ leftPadding,
+ /* top=*/0,
+ /* right=*/0,
+ /* bottom=*/0)
remoteViews.setViewPadding(
- android.R.id.text1,
- iconToTextPadding,
- /* top=*/topPadding,
- /* right=*/rightPadding,
- primaryTextBottomPadding)
+ android.R.id.text1,
+ iconToTextPadding,
+ /* top=*/topPadding,
+ /* right=*/rightPadding,
+ primaryTextBottomPadding)
remoteViews.setViewPadding(
- android.R.id.text2,
- iconToTextPadding,
- /* top=*/0,
- /* right=*/rightPadding,
- /* bottom=*/bottomPadding)
+ android.R.id.text2,
+ iconToTextPadding,
+ /* top=*/0,
+ /* right=*/rightPadding,
+ /* bottom=*/bottomPadding)
}
private fun isDarkMode(context: Context): Boolean {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index d24adb567bc4..af78573ee9e9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager.createflow
+import android.credentials.flags.Flags.selectorUiImprovementsEnabled
import android.text.TextUtils
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
@@ -329,6 +330,18 @@ fun CreationSelectionCard(
)
}
item { Divider(thickness = 24.dp, color = Color.Transparent) }
+
+ val footerDescription = createOptionInfo.footerDescription
+ if (selectorUiImprovementsEnabled()) {
+ if (!footerDescription.isNullOrBlank()) {
+ item {
+ Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+ BodyMediumText(text = footerDescription)
+ }
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ }
+ }
item {
CredentialContainerCard {
PrimaryCreateOptionRow(
@@ -366,18 +379,19 @@ fun CreationSelectionCard(
},
)
}
- val footerDescription = createOptionInfo.footerDescription
- if (footerDescription != null && footerDescription.length > 0) {
- item {
- Divider(
- thickness = 1.dp,
- color = LocalAndroidColorScheme.current.outlineVariant,
- modifier = Modifier.padding(vertical = 16.dp)
- )
- }
- item {
- Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
- BodySmallText(text = footerDescription)
+ if (!selectorUiImprovementsEnabled()) {
+ if (footerDescription != null && footerDescription.length > 0) {
+ item {
+ Divider(
+ thickness = 1.dp,
+ color = LocalAndroidColorScheme.current.outlineVariant,
+ modifier = Modifier.padding(vertical = 16.dp)
+ )
+ }
+ item {
+ Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+ BodySmallText(text = footerDescription)
+ }
}
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index ccc46604ff98..bc0ea02d5b15 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager.getflow
+import android.credentials.flags.Flags.selectorUiImprovementsEnabled
import android.graphics.drawable.Drawable
import android.text.TextUtils
import androidx.activity.compose.ManagedActivityResultLauncher
@@ -40,6 +41,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextLayoutResult
@@ -75,6 +77,7 @@ import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.credentialmanager.userAndDisplayNameForPasskey
import com.android.internal.logging.UiEventLogger.UiEventEnum
+import kotlin.math.max
@Composable
fun GetCredentialScreen(
@@ -110,16 +113,29 @@ fun GetCredentialScreen(
ProviderActivityState.NOT_APPLICABLE -> {
if (getCredentialUiState.currentScreenState
== GetScreenState.PRIMARY_SELECTION) {
- PrimarySelectionCard(
- requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
- providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
- providerInfoList = getCredentialUiState.providerInfoList,
- activeEntry = getCredentialUiState.activeEntry,
- onEntrySelected = viewModel::getFlowOnEntrySelected,
- onConfirm = viewModel::getFlowOnConfirmEntrySelected,
- onMoreOptionSelected = viewModel::getFlowOnMoreOptionSelected,
- onLog = { viewModel.logUiEvent(it) },
- )
+ if (selectorUiImprovementsEnabled()) {
+ PrimarySelectionCardVImpl(
+ requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
+ providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
+ providerInfoList = getCredentialUiState.providerInfoList,
+ activeEntry = getCredentialUiState.activeEntry,
+ onEntrySelected = viewModel::getFlowOnEntrySelected,
+ onConfirm = viewModel::getFlowOnConfirmEntrySelected,
+ onMoreOptionSelected = viewModel::getFlowOnMoreOptionSelected,
+ onLog = { viewModel.logUiEvent(it) },
+ )
+ } else {
+ PrimarySelectionCard(
+ requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
+ providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
+ providerInfoList = getCredentialUiState.providerInfoList,
+ activeEntry = getCredentialUiState.activeEntry,
+ onEntrySelected = viewModel::getFlowOnEntrySelected,
+ onConfirm = viewModel::getFlowOnConfirmEntrySelected,
+ onMoreOptionSelected = viewModel::getFlowOnMoreOptionSelected,
+ onLog = { viewModel.logUiEvent(it) },
+ )
+ }
viewModel.uiMetrics.log(GetCredentialEvent
.CREDMAN_GET_CRED_SCREEN_PRIMARY_SELECTION)
} else {
@@ -174,7 +190,8 @@ fun GetCredentialScreen(
}
}
-/** Draws the primary credential selection page. */
+/** Draws the primary credential selection page, used in Android U. */
+// TODO(b/327518384) - remove after flag selectorUiImprovementsEnabled is enabled.
@Composable
fun PrimarySelectionCard(
requestDisplayInfo: RequestDisplayInfo,
@@ -358,6 +375,202 @@ fun PrimarySelectionCard(
onLog(GetCredentialEvent.CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD)
}
+internal const val MAX_ENTRY_FOR_PRIMARY_PAGE = 4
+
+/** Draws the primary credential selection page, used starting from android V. */
+@Composable
+fun PrimarySelectionCardVImpl(
+ requestDisplayInfo: RequestDisplayInfo,
+ providerDisplayInfo: ProviderDisplayInfo,
+ providerInfoList: List<ProviderInfo>,
+ activeEntry: EntryInfo?,
+ onEntrySelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onMoreOptionSelected: () -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
+) {
+ val showMoreForTruncatedEntry = remember { mutableStateOf(false) }
+ val sortedUserNameToCredentialEntryList =
+ providerDisplayInfo.sortedUserNameToCredentialEntryList
+ val authenticationEntryList = providerDisplayInfo.authenticationEntryList
+ // Show at most 4 entries (credential type or locked type) in this primary page
+ val primaryPageCredentialEntryList =
+ sortedUserNameToCredentialEntryList.take(MAX_ENTRY_FOR_PRIMARY_PAGE)
+ val primaryPageLockedEntryList = authenticationEntryList.take(
+ max(0, MAX_ENTRY_FOR_PRIMARY_PAGE - primaryPageCredentialEntryList.size)
+ )
+ SheetContainerCard {
+ val preferTopBrandingContent = requestDisplayInfo.preferTopBrandingContent
+ val singleProviderId = findSingleProviderIdForPrimaryPage(
+ primaryPageCredentialEntryList,
+ primaryPageLockedEntryList
+ )
+ if (preferTopBrandingContent != null) {
+ item {
+ HeadlineProviderIconAndName(
+ preferTopBrandingContent.icon,
+ preferTopBrandingContent.displayName
+ )
+ }
+ } else {
+ // When only one provider's entries will be displayed on the primary page, display that
+ // provider's icon + name up top.
+ if (singleProviderId != null) {
+ // First should always work but just to be safe.
+ val providerInfo = providerInfoList.firstOrNull { it.id == singleProviderId }
+ if (providerInfo != null) {
+ item {
+ HeadlineProviderIconAndName(
+ providerInfo.icon,
+ providerInfo.displayName
+ )
+ }
+ }
+ }
+ }
+
+ val hasSingleEntry = primaryPageCredentialEntryList.size +
+ primaryPageLockedEntryList.size == 1
+ item {
+ if (requestDisplayInfo.preferIdentityDocUi) {
+ HeadlineText(
+ text = stringResource(
+ if (hasSingleEntry) {
+ R.string.get_dialog_title_use_info_on
+ } else {
+ R.string.get_dialog_title_choose_option_for
+ },
+ requestDisplayInfo.appName
+ ),
+ )
+ } else {
+ HeadlineText(
+ text = stringResource(
+ if (hasSingleEntry) {
+ val singleEntryType = primaryPageCredentialEntryList.firstOrNull()
+ ?.sortedCredentialEntryList?.firstOrNull()?.credentialType
+ if (singleEntryType == CredentialType.PASSKEY)
+ R.string.get_dialog_title_use_passkey_for
+ else if (singleEntryType == CredentialType.PASSWORD)
+ R.string.get_dialog_title_use_password_for
+ else if (authenticationEntryList.isNotEmpty())
+ R.string.get_dialog_title_unlock_options_for
+ else R.string.get_dialog_title_use_sign_in_for
+ } else {
+ if (authenticationEntryList.isNotEmpty() ||
+ sortedUserNameToCredentialEntryList.any { perNameEntryList ->
+ perNameEntryList.sortedCredentialEntryList.any { entry ->
+ entry.credentialType != CredentialType.PASSWORD &&
+ entry.credentialType != CredentialType.PASSKEY
+ }
+ }
+ ) // For an unknown / locked entry, it's not true that it is
+ // already saved, strictly speaking. Hence use a different title
+ // without the mention of "saved"
+ R.string.get_dialog_title_choose_sign_in_for
+ else
+ R.string.get_dialog_title_choose_saved_sign_in_for
+ },
+ requestDisplayInfo.appName
+ ),
+ )
+ }
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item {
+ CredentialContainerCard {
+ Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
+ primaryPageCredentialEntryList.forEach {
+ val entry = it.sortedCredentialEntryList.first()
+ CredentialEntryRow(
+ credentialEntryInfo = entry,
+ onEntrySelected = onEntrySelected,
+ enforceOneLine = true,
+ onTextLayout = {
+ showMoreForTruncatedEntry.value = it.hasVisualOverflow
+ },
+ hasSingleEntry = hasSingleEntry,
+ shouldOverrideIcon = entry.isDefaultIconPreferredAsSingleProvider &&
+ (singleProviderId != null),
+ )
+ }
+ primaryPageLockedEntryList.forEach {
+ AuthenticationEntryRow(
+ authenticationEntryInfo = it,
+ onEntrySelected = onEntrySelected,
+ enforceOneLine = true,
+ )
+ }
+ }
+ }
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ var totalEntriesCount = sortedUserNameToCredentialEntryList
+ .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList
+ .size + providerInfoList.flatMap { it.actionEntryList }.size
+ if (providerDisplayInfo.remoteEntry != null) totalEntriesCount += 1
+ // Row horizontalArrangement differs on only one actionButton(should place on most
+ // left)/only one confirmButton(should place on most right)/two buttons exist the same
+ // time(should be one on the left, one on the right)
+ item {
+ CtaButtonRow(
+ leftButton = if (totalEntriesCount > 1) {
+ {
+ ActionButton(
+ stringResource(R.string.get_dialog_title_sign_in_options),
+ onMoreOptionSelected
+ )
+ }
+ } else if (showMoreForTruncatedEntry.value) {
+ {
+ ActionButton(
+ stringResource(R.string.button_label_view_more),
+ onMoreOptionSelected
+ )
+ }
+ } else null,
+ rightButton = if (activeEntry != null) { // Only one sign-in options exist
+ {
+ ConfirmButton(
+ stringResource(R.string.string_continue),
+ onClick = onConfirm
+ )
+ }
+ } else null,
+ )
+ }
+ }
+ onLog(GetCredentialEvent.CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD)
+}
+
+/**
+ * Attempt to find a single provider id, if it has supplied all the entries to be displayed on the
+ * front page; otherwise if multiple providers are found, return null.
+ */
+private fun findSingleProviderIdForPrimaryPage(
+ primaryPageCredentialEntryList: List<PerUserNameCredentialEntryList>,
+ primaryPageLockedEntryList: List<AuthenticationEntryInfo>
+): String? {
+ var providerId: String? = null
+ primaryPageCredentialEntryList.forEach {
+ val currProviderId = it.sortedCredentialEntryList.first().providerId
+ if (providerId == null) {
+ providerId = currProviderId
+ } else if (providerId != currProviderId) {
+ return null
+ }
+ }
+ primaryPageLockedEntryList.forEach {
+ val currProviderId = it.providerId
+ if (providerId == null) {
+ providerId = currProviderId
+ } else if (providerId != currProviderId) {
+ return null
+ }
+ }
+ return providerId
+}
+
/** Draws the secondary credential selection page, where all sign-in options are listed. */
@Composable
fun AllSignInOptionCard(
@@ -540,25 +753,52 @@ fun CredentialEntryRow(
onEntrySelected: (EntryInfo) -> Unit,
enforceOneLine: Boolean = false,
onTextLayout: (TextLayoutResult) -> Unit = {},
+ // Make optional since the secondary page doesn't care about this value.
+ hasSingleEntry: Boolean? = null,
+ // For primary page only, if all display entries come from the same provider AND if that
+ // provider has opted in via isDefaultIconPreferredAsSingleProvider, then we override the
+ // display icon to the default icon for the given credential type.
+ shouldOverrideIcon: Boolean = false,
) {
val (username, displayName) = if (credentialEntryInfo.credentialType == CredentialType.PASSKEY)
userAndDisplayNameForPasskey(
credentialEntryInfo.userName, credentialEntryInfo.displayName ?: "")
else Pair(credentialEntryInfo.userName, credentialEntryInfo.displayName)
+
+ // For primary page, if
+ val overrideIcon: Painter? =
+ if (shouldOverrideIcon) {
+ when (credentialEntryInfo.credentialType) {
+ CredentialType.PASSKEY -> painterResource(R.drawable.ic_passkey_24)
+ CredentialType.PASSWORD -> painterResource(R.drawable.ic_password_24)
+ else -> painterResource(R.drawable.ic_other_sign_in_24)
+ }
+ } else null
+
Entry(
onClick = { onEntrySelected(credentialEntryInfo) },
- iconImageBitmap = credentialEntryInfo.icon?.toBitmap()?.asImageBitmap(),
+ iconImageBitmap =
+ if (overrideIcon == null) credentialEntryInfo.icon?.toBitmap()?.asImageBitmap() else null,
shouldApplyIconImageBitmapTint = credentialEntryInfo.shouldTintIcon,
// Fall back to iconPainter if iconImageBitmap isn't available
iconPainter =
- if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24)
+ if (overrideIcon != null) overrideIcon
+ else if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24)
else null,
entryHeadlineText = username,
- entrySecondLineText = listOf(
- displayName,
+ entrySecondLineText = displayName,
+ entryThirdLineText =
+ (if (hasSingleEntry != null && hasSingleEntry)
+ if (credentialEntryInfo.credentialType == CredentialType.PASSKEY ||
+ credentialEntryInfo.credentialType == CredentialType.PASSWORD)
+ emptyList()
+ // Still show the type display name for all non-password/passkey types since it won't be
+ // mentioned in the bottom sheet heading.
+ else listOf(credentialEntryInfo.credentialTypeDisplayName)
+ else listOf(
credentialEntryInfo.credentialTypeDisplayName,
credentialEntryInfo.providerDisplayName
- ).filterNot(TextUtils::isEmpty).let { itemsToDisplay ->
+ )).filterNot(TextUtils::isEmpty).let { itemsToDisplay ->
if (itemsToDisplay.isEmpty()) null
else itemsToDisplay.joinToString(
separator = stringResource(R.string.get_dialog_sign_in_type_username_separator)
@@ -566,6 +806,7 @@ fun CredentialEntryRow(
},
enforceOneLine = enforceOneLine,
onTextLayout = onTextLayout,
+ affiliatedDomainText = credentialEntryInfo.affiliatedDomain,
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 458a99a8cd2f..e35acae547a6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -16,20 +16,23 @@
package com.android.credentialmanager.getflow
+import android.credentials.flags.Flags.selectorUiImprovementsEnabled
import android.graphics.drawable.Drawable
+import androidx.credentials.PriorityHints
import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.model.EntryInfo
-import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.model.get.AuthenticationEntryInfo
import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.internal.util.Preconditions
+import java.time.Instant
data class GetCredentialUiState(
val isRequestForAllOptions: Boolean,
val providerInfoList: List<ProviderInfo>,
val requestDisplayInfo: RequestDisplayInfo,
- val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
+ val providerDisplayInfo: ProviderDisplayInfo =
+ toProviderDisplayInfo(providerInfoList, requestDisplayInfo.typePriorityMap),
val currentScreenState: GetScreenState = toGetScreenState(
providerDisplayInfo, isRequestForAllOptions),
val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
@@ -78,6 +81,8 @@ data class RequestDisplayInfo(
val preferIdentityDocUi: Boolean,
// A top level branding icon + display name preferred by the app.
val preferTopBrandingContent: TopBrandingContent?,
+ // Map of credential type -> priority.
+ val typePriorityMap: Map<String, Int>,
)
data class TopBrandingContent(
@@ -118,7 +123,8 @@ enum class GetScreenState {
* @hide
*/
fun toProviderDisplayInfo(
- providerInfoList: List<ProviderInfo>
+ providerInfoList: List<ProviderInfo>,
+ typePriorityMap: Map<String, Int>,
): ProviderDisplayInfo {
val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>()
val authenticationEntryList = mutableListOf<AuthenticationEntryInfo>()
@@ -133,7 +139,7 @@ fun toProviderDisplayInfo(
providerInfo.credentialEntryList.forEach {
userNameToCredentialEntryMap.compute(
- it.userName
+ if (selectorUiImprovementsEnabled()) it.entryGroupId else it.userName
) { _, v ->
if (v == null) {
mutableListOf(it)
@@ -146,16 +152,22 @@ fun toProviderDisplayInfo(
}
// Compose sortedUserNameToCredentialEntryList
- val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp()
+ val comparator = CredentialEntryInfoComparatorByTypeThenTimestamp(typePriorityMap)
// Sort per username
userNameToCredentialEntryMap.values.forEach {
it.sortWith(comparator)
}
- // Transform to list of PerUserNameCredentialEntryLists and then sort across usernames
+ // Transform to list of PerUserNameCredentialEntryLists and then sort the outer list (of
+ // entries grouped by username / entryGroupId) based on the latest timestamp within that
+ // PerUserNameCredentialEntryList
val sortedUserNameToCredentialEntryList = userNameToCredentialEntryMap.map {
PerUserNameCredentialEntryList(it.key, it.value)
}.sortedWith(
- compareByDescending { it.sortedCredentialEntryList.first().lastUsedTimeMillis }
+ compareByDescending {
+ it.sortedCredentialEntryList.maxByOrNull{ entry ->
+ entry.lastUsedTimeMillis ?: Instant.MIN
+ }?.lastUsedTimeMillis ?: Instant.MIN
+ }
)
return ProviderDisplayInfo(
@@ -202,16 +214,25 @@ private fun toGetScreenState(
else GetScreenState.PRIMARY_SELECTION
}
-internal class CredentialEntryInfoComparatorByTypeThenTimestamp : Comparator<CredentialEntryInfo> {
+internal class CredentialEntryInfoComparatorByTypeThenTimestamp(
+ val typePriorityMap: Map<String, Int>,
+) : Comparator<CredentialEntryInfo> {
override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int {
- // First prefer passkey type for its security benefits
- if (p0.credentialType != p1.credentialType) {
- if (CredentialType.PASSKEY == p0.credentialType) {
+ // First rank by priorities of each credential type.
+ if (p0.rawCredentialType != p1.rawCredentialType) {
+ val p0Priority = typePriorityMap.getOrDefault(
+ p0.rawCredentialType, PriorityHints.PRIORITY_DEFAULT
+ )
+ val p1Priority = typePriorityMap.getOrDefault(
+ p1.rawCredentialType, PriorityHints.PRIORITY_DEFAULT
+ )
+ if (p0Priority < p1Priority) {
return -1
- } else if (CredentialType.PASSKEY == p1.credentialType) {
+ } else if (p1Priority < p0Priority) {
return 1
}
}
+ // Then rank by last used timestamps.
val p0LastUsedTimeMillis = p0.lastUsedTimeMillis
val p1LastUsedTimeMillis = p1.lastUsedTimeMillis
// Then order by last used timestamp
diff --git a/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt b/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt
index e609d0c5c008..28d83ee157b6 100644
--- a/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt
+++ b/packages/CredentialManager/tests/robotests/screenshot/src/com/android/credentialmanager/GetCredScreenshotTest.kt
@@ -53,6 +53,7 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) {
preferImmediatelyAvailableCredentials = false,
preferIdentityDocUi = false,
preferTopBrandingContent = null,
+ typePriorityMap = emptyMap(),
)
}
@@ -68,7 +69,7 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) {
fun singleCredentialScreen_M3BottomSheetDisabled() {
setFlagsRule.disableFlags(Flags.FLAG_SELECTOR_UI_IMPROVEMENTS_ENABLED)
val providerInfoList = buildProviderInfoList()
- val providerDisplayInfo = toProviderDisplayInfo(providerInfoList)
+ val providerDisplayInfo = toProviderDisplayInfo(providerInfoList, emptyMap())
val activeEntry = toActiveEntry(providerDisplayInfo)
screenshotRule.screenshotTest("singleCredentialScreen") {
ModalBottomSheet(
@@ -96,7 +97,7 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) {
fun singleCredentialScreen_M3BottomSheetEnabled() {
setFlagsRule.enableFlags(Flags.FLAG_SELECTOR_UI_IMPROVEMENTS_ENABLED)
val providerInfoList = buildProviderInfoList()
- val providerDisplayInfo = toProviderDisplayInfo(providerInfoList)
+ val providerDisplayInfo = toProviderDisplayInfo(providerInfoList, emptyMap())
val activeEntry = toActiveEntry(providerDisplayInfo)
screenshotRule.screenshotTest(
"singleCredentialScreen_newM3BottomSheet",
@@ -146,7 +147,11 @@ class GetCredScreenshotTest(emulationSpec: DeviceEmulationSpec) {
icon = null,
shouldTintIcon = true,
lastUsedTimeMillis = null,
- isAutoSelectable = false
+ isAutoSelectable = false,
+ entryGroupId = "username",
+ isDefaultIconPreferredAsSingleProvider = false,
+ rawCredentialType = "unknown-type",
+ affiliatedDomain = null,
)
),
authenticationEntryList = emptyList(),
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 463c4d1b063e..66be7ba5e079 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -29,10 +29,14 @@ import com.android.credentialmanager.model.get.AuthenticationEntryInfo
import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.mappers.toGet
import android.util.Log
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.compose.runtime.Composable
import com.android.credentialmanager.CredentialSelectorUiState.Cancel
import com.android.credentialmanager.CredentialSelectorUiState.Close
import com.android.credentialmanager.CredentialSelectorUiState.Create
import com.android.credentialmanager.CredentialSelectorUiState.Idle
+import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
+import com.android.credentialmanager.ktx.getIntentSenderRequest
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -47,6 +51,8 @@ class CredentialSelectorViewModel @Inject constructor(
) : FlowEngine, ViewModel() {
private val isPrimaryScreen = MutableStateFlow(true)
private val shouldClose = MutableStateFlow(false)
+ private lateinit var selectedEntry: EntryInfo
+ private var isAutoSelected: Boolean = false
val uiState: StateFlow<CredentialSelectorUiState> =
combine(
credentialManagerClient.requests,
@@ -108,6 +114,25 @@ class CredentialSelectorViewModel @Inject constructor(
)
shouldClose.value = result
}
+
+ @Composable
+ override fun getEntrySelector(): (entry: EntryInfo, isAutoSelected: Boolean) -> Unit {
+ val launcher = rememberLauncherForActivityResult(
+ StartBalIntentSenderForResultContract()
+ ) {
+ sendSelectionResult(entryInfo = selectedEntry,
+ resultCode = it.resultCode,
+ resultData = it.data,
+ isAutoSelected = isAutoSelected)
+ }
+ return { selected, autoSelect ->
+ selectedEntry = selected
+ isAutoSelected = autoSelect
+ selected.getIntentSenderRequest()?.let {
+ launcher.launch(it)
+ } ?: Log.w(TAG, "Cannot parse IntentSenderRequest")
+ }
+ }
}
sealed class CredentialSelectorUiState {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
index e4216446772b..2e80a7c672f4 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
@@ -17,6 +17,8 @@
package com.android.credentialmanager
import android.content.Intent
+import androidx.activity.result.IntentSenderRequest
+import androidx.compose.runtime.Composable
import com.android.credentialmanager.model.EntryInfo
/** Engine of the credential selecting flow. */
@@ -42,4 +44,14 @@ interface FlowEngine {
resultData: Intent? = null,
isAutoSelected: Boolean = false,
)
+
+ /**
+ * Helper function to get an entry selector.
+ *
+ * @return selector fun consumes selected [EntryInfo]. Once invoked, [IntentSenderRequest] would
+ * be launched and invocation of [sendSelectionResult] would happen right after launching result
+ * coming back.
+ */
+ @Composable
+ fun getEntrySelector(): (entry: EntryInfo, isAutoSelected: Boolean) -> Unit
} \ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index f9a5887158eb..405de1d3acc6 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -43,6 +43,7 @@ import com.google.android.horologist.compose.navscaffold.WearNavScaffold
import com.google.android.horologist.compose.navscaffold.composable
import com.google.android.horologist.compose.navscaffold.scrollable
import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.ui.screens.multiple.MultiCredentialsFoldScreen
@OptIn(ExperimentalHorologistApi::class)
@@ -56,6 +57,7 @@ fun WearApp(
val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
val navHostState =
rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
+ val selectEntry = flowEngine.getEntrySelector()
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
WearNavScaffold(
@@ -111,6 +113,7 @@ fun WearApp(
navController = navController,
state = state,
onCloseApp = onCloseApp,
+ selectEntry = selectEntry
)
}
@@ -133,9 +136,14 @@ private fun handleGetNavigation(
navController: NavController,
state: CredentialSelectorUiState.Get,
onCloseApp: () -> Unit,
+ selectEntry: (entry: EntryInfo, isAutoSelected: Boolean) -> Unit,
) {
when (state) {
is SingleEntry -> {
+ if (state.entry.isAutoSelectable) {
+ selectEntry(state.entry, true)
+ return
+ }
when (state.entry.credentialType) {
CredentialType.UNKNOWN -> {
navController.navigateToSignInWithProviderScreen()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index 1f1a296dca9b..2ca8ef13c0cf 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -18,8 +18,6 @@
package com.android.credentialmanager.ui.screens.single.password
-import android.util.Log
-import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
@@ -28,9 +26,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.credentialmanager.FlowEngine
import com.android.credentialmanager.R
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
-import com.android.credentialmanager.ktx.getIntentSenderRequest
import com.android.credentialmanager.ui.components.PasswordRow
import com.android.credentialmanager.ui.components.ContinueChip
import com.android.credentialmanager.ui.components.DismissChip
@@ -57,11 +52,7 @@ fun SinglePasswordScreen(
modifier: Modifier = Modifier,
flowEngine: FlowEngine,
) {
- val launcher = rememberLauncherForActivityResult(
- StartBalIntentSenderForResultContract()
- ) {
- flowEngine.sendSelectionResult(entry, it.resultCode, it.data)
- }
+ val selectEntry = flowEngine.getEntrySelector()
SingleAccountScreen(
headerContent = {
SignInHeader(
@@ -80,11 +71,7 @@ fun SinglePasswordScreen(
) {
item {
Column {
- ContinueChip {
- entry.getIntentSenderRequest()?.let {
- launcher.launch(it)
- } ?: Log.w(TAG, "Cannot parse IntentSenderRequest")
- }
+ ContinueChip { selectEntry(entry, false) }
SignInOptionsChip{ flowEngine.openSecondaryScreen() }
DismissChip { flowEngine.cancel() }
}
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
index 0caf505c2177..6f4f9ca2e8e1 100644
--- a/packages/EasterEgg/Android.bp
+++ b/packages/EasterEgg/Android.bp
@@ -48,6 +48,8 @@ android_app {
},
static_libs: [
+ "easter_egg_flags_lib",
+
"androidx.core_core",
"androidx.annotation_annotation",
"androidx.recyclerview_recyclerview",
@@ -72,3 +74,16 @@ android_app {
kotlincflags: ["-Xjvm-default=all"],
}
+
+java_aconfig_library {
+ name: "easter_egg_flags_lib",
+ aconfig_declarations: "easter_egg_flags",
+}
+
+aconfig_declarations {
+ name: "easter_egg_flags",
+ package: "com.android.egg.flags",
+ srcs: [
+ "easter_egg_flags.aconfig",
+ ],
+}
diff --git a/packages/EasterEgg/easter_egg_flags.aconfig b/packages/EasterEgg/easter_egg_flags.aconfig
new file mode 100644
index 000000000000..3268a4f5728c
--- /dev/null
+++ b/packages/EasterEgg/easter_egg_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.egg.flags"
+
+flag {
+ name: "flag_flag"
+ namespace: "systemui"
+ description: "Flags are planted on planets when you land. Yes, it's a flag for flags."
+ bug: "320150798"
+}
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
index fec3ab3877ea..11dce613b0fb 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
@@ -174,7 +174,9 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed)
ship = Spacecraft()
- ship.pos = star.pos + Vec2.makeWithAngleMag(PIf / 4, PLANET_ORBIT_RANGE.start)
+ // in the test universe, start the ship near the outermost planet
+ ship.pos = planets.last().pos + Vec2(planets.first().radius * 1.5f, 0f)
+
ship.angle = 0f
add(ship)
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
index 24b9c6a283c2..6baf36ee3c8a 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
@@ -31,6 +31,8 @@ import androidx.core.math.MathUtils.clamp
import java.lang.Float.max
import kotlin.math.sqrt
+import com.android.egg.flags.Flags.flagFlag
+
const val DRAW_ORBITS = true
const val DRAW_GRAVITATIONAL_FIELDS = true
const val DRAW_STAR_GRAVITATIONAL_FIELDS = true
@@ -279,8 +281,23 @@ fun ZoomedDrawScope.drawSpacecraft(ship: Spacecraft) {
fun ZoomedDrawScope.drawLanding(landing: Landing) {
val v = landing.planet.pos + Vec2.makeWithAngleMag(landing.angle, landing.planet.radius)
- drawLine(Color.Red, v + Vec2(-5f, -5f), v + Vec2(5f, 5f), strokeWidth = 1f / zoom)
- drawLine(Color.Red, v + Vec2(5f, -5f), v + Vec2(-5f, 5f), strokeWidth = 1f / zoom)
+
+ if (flagFlag()) {
+ val strokeWidth = 2f / zoom
+ val height = 80f
+ rotateRad(landing.angle, pivot = v) {
+ translate(v.x, v.y) {
+ drawPath(
+ Path().apply {
+ moveTo(0f, 0f)
+ lineTo(height, 0f)
+ lineTo(height * 0.875f, height * 0.25f)
+ lineTo(height * 0.75f, 0f)
+ close()
+ }, Color.Yellow, style = Stroke(width = strokeWidth))
+ }
+ }
+ }
}
fun ZoomedDrawScope.drawSpark(spark: Spark) {
diff --git a/packages/PackageInstaller/res/layout/install_content_view.xml b/packages/PackageInstaller/res/layout/install_content_view.xml
index 2ecd2d55ac71..524a88a638ad 100644
--- a/packages/PackageInstaller/res/layout/install_content_view.xml
+++ b/packages/PackageInstaller/res/layout/install_content_view.xml
@@ -24,114 +24,116 @@
android:paddingLeft="?android:attr/dialogPreferredPadding"
android:paddingRight="?android:attr/dialogPreferredPadding">
- <LinearLayout
- android:id="@+id/staging"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="invisible">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/message_staging" />
-
- <ProgressBar
- android:id="@+id/progress_indeterminate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- style="?android:attr/progressBarStyleHorizontal"
- android:indeterminate="true" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/installing"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="invisible">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/installing" />
-
- <ProgressBar
- android:id="@+id/progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- style="?android:attr/progressBarStyleHorizontal"
- android:indeterminate="true" />
-
- </LinearLayout>
-
- <TextView
- android:id="@+id/install_confirm_question"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_confirm_question"
- android:visibility="invisible" />
-
- <TextView
- android:id="@+id/install_confirm_question_update"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_confirm_question_update"
- android:visibility="invisible" />
+ <LinearLayout
+ android:id="@+id/staging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="invisible">
<TextView
- android:id="@+id/install_success"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_done"
- android:visibility="invisible" />
+ android:text="@string/message_staging" />
- <TextView
- android:id="@+id/install_failed"
- android:layout_width="wrap_content"
+ <ProgressBar
+ android:id="@+id/progress_indeterminate"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed"
- android:visibility="invisible" />
+ android:paddingTop="8dp"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:indeterminate="true" />
- <TextView
- android:id="@+id/install_failed_blocked"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_blocked"
- android:visibility="invisible" />
+ </LinearLayout>
- <TextView
- android:id="@+id/install_failed_conflict"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_conflict"
- android:visibility="invisible" />
+ <LinearLayout
+ android:id="@+id/installing"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="invisible">
<TextView
- android:id="@+id/install_failed_incompatible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_incompatible"
- android:visibility="invisible" />
+ android:text="@string/installing" />
- <TextView
- android:id="@+id/install_failed_invalid_apk"
- android:layout_width="wrap_content"
+ <ProgressBar
+ android:id="@+id/progress"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_invalid_apk"
- android:visibility="invisible" />
-
-</FrameLayout>
+ android:paddingTop="8dp"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:indeterminate="true" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/install_confirm_question"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_confirm_question"
+ android:visibility="invisible"
+ android:scrollbars="vertical" />
+
+ <TextView
+ android:id="@+id/install_confirm_question_update"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_confirm_question_update"
+ android:visibility="invisible"
+ android:scrollbars="vertical" />
+
+ <TextView
+ android:id="@+id/install_success"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_done"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/install_failed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/install_failed_blocked"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_blocked"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/install_failed_conflict"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_conflict"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/install_failed_incompatible"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_incompatible"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/install_failed_invalid_apk"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_invalid_apk"
+ android:visibility="invisible" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
index 5666c0e44e0b..434e33323ba1 100644
--- a/packages/PackageInstaller/res/layout/uninstall_content_view.xml
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -18,31 +18,36 @@
<!-- Check box that is displayed in the activity resolver UI for the user
to make their selection the preferred activity. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:theme="?android:attr/alertDialogTheme"
- android:orientation="vertical"
- android:paddingTop="8dp"
- android:paddingStart="?android:attr/dialogPreferredPadding"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:clipToPadding="false">
+ android:layout_height="wrap_content">
- <TextView
- android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/alertDialogTheme"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:clipToPadding="false">
- <CheckBox
- android:id="@+id/keepData"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="-8dp"
- android:paddingLeft="8sp"
- android:visibility="gone"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead" />
-</LinearLayout> \ No newline at end of file
+ <CheckBox
+ android:id="@+id/keepData"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingLeft="8sp"
+ android:visibility="gone"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index 61aa4e65128a..3c37df68456c 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -117,7 +117,7 @@
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Κάτι πήγε στραβά"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Παρουσιάστηκε κάποιο πρόβλημα κατά την επαναφορά αυτής της εφαρμογής"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Δεν επαρκεί ο αποθηκευτικός χώρος"</string>
- <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Για να επαναφέρετε αυτή την εφαρμογή, μπορείτε να ελευθερώσετε χώρο στη συσκευή. Απαιτούμενος αποθηκευτικός χώρος: <xliff:g id="BYTES">%1$s</xliff:g>"</string>
+ <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Για να επαναφέρετε αυτή την εφαρμογή, μπορείτε να αποδεσμεύσετε χώρο στη συσκευή. Απαιτούμενος αποθηκευτικός χώρος: <xliff:g id="BYTES">%1$s</xliff:g>"</string>
<string name="unarchive_action_required_title" msgid="4971245740162604619">"Απαιτούμενη ενέργεια"</string>
<string name="unarchive_action_required_body" msgid="1679431572983989231">"Ακολουθήστε τα επόμενα βήματα για να επαναφέρετε αυτή την εφαρμογή"</string>
<string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Το <xliff:g id="INSTALLERNAME">%1$s</xliff:g> είναι απενεργοποιημένο"</string>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 531190327165..ee0c752c54b0 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -98,9 +98,9 @@
<string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Pour votre sécurité, l\'installation d\'applis inconnues provenant de cette source n\'est pas autorisée sur ce téléviseur actuellement. Vous pouvez modifier cette option dans les paramètres."</string>
<string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"Pour votre sécurité, l\'installation d\'applis inconnues provenant de cette source n\'est pas autorisée sur cette montre actuellement. Vous pouvez modifier cette option dans les paramètres."</string>
<string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Pour votre sécurité, l\'installation d\'applis inconnues provenant de cette source n\'est pas autorisée sur ce téléphone actuellement. Vous pouvez modifier cette option dans les paramètres."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Votre téléphone et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de son utilisation."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de son utilisation."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Votre téléphone et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être l\'unique responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de son utilisation."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être l\'unique responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de son utilisation."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être l\'unique responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
<string name="archiving_app_label" msgid="1127085259724124725">"Archiver <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
index 634e067a12d4..cf2f85ed5356 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -20,7 +20,6 @@ import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
-import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -28,10 +27,10 @@ import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
+import android.Manifest;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -201,7 +200,7 @@ public class InstallStaging extends Activity {
params.setPermissionState(Manifest.permission.USE_FULL_SCREEN_INTENT,
PackageInstaller.SessionParams.PERMISSION_STATE_DENIED);
- if (pfd != null && Flags.readInstallInfo()) {
+ if (pfd != null) {
try {
final PackageInstaller.InstallInfo result = installer.readInstallInfo(pfd,
debugPathName, 0);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index cf6aab641fc9..45bfe5469172 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -31,7 +31,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.Flags;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
@@ -51,6 +50,7 @@ import android.provider.Settings;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
@@ -174,6 +174,7 @@ public class PackageInstallerActivity extends Activity {
}
viewToEnable.setVisibility(View.VISIBLE);
+ viewToEnable.setMovementMethod(new ScrollingMovementMethod());
mEnableOk = true;
mOk.setEnabled(true);
@@ -398,10 +399,7 @@ public class PackageInstallerActivity extends Activity {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID,
-1 /* defaultValue */);
final SessionInfo info = mInstaller.getSessionInfo(sessionId);
- String resolvedPath = null;
- if (info != null && Flags.getResolvedApkPath()) {
- resolvedPath = info.getResolvedBaseApkPath();
- }
+ String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null;
if (info == null || !info.isSealed() || resolvedPath == null) {
Log.w(TAG, "Session " + sessionId + " in funky state; ignoring");
finish();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 22caabdabbf0..aeabbd53d177 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -25,7 +25,6 @@ import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
-import android.content.pm.Flags
import android.content.pm.PackageInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageInstaller.SessionInfo
@@ -363,7 +362,7 @@ class InstallRepository(private val context: Context) {
params.setPermissionState(
Manifest.permission.USE_FULL_SCREEN_INTENT, SessionParams.PERMISSION_STATE_DENIED
)
- if (pfd != null && Flags.readInstallInfo()) {
+ if (pfd != null) {
try {
val installInfo = packageInstaller.readInstallInfo(pfd, debugPathName, 0)
params.setAppPackageName(installInfo.packageName)
@@ -426,8 +425,7 @@ class InstallRepository(private val context: Context) {
if (PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action) {
val info = packageInstaller.getSessionInfo(sessionId)
- val resolvedPath =
- if (Flags.getResolvedApkPath()) info?.resolvedBaseApkPath else null
+ val resolvedPath = info?.resolvedBaseApkPath
if (info == null || !info.isSealed || resolvedPath == null) {
Log.w(LOG_TAG, "Session $sessionId in funky state; ignoring")
return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index dbe32cc42d1a..0a4aa48fc126 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Html;
+import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -94,6 +95,7 @@ public class InstallConfirmationFragment extends DialogFragment {
viewToEnable = dialogView.requireViewById(R.id.install_confirm_question);
}
viewToEnable.setVisibility(View.VISIBLE);
+ viewToEnable.setMovementMethod(new ScrollingMovementMethod());
return mDialog;
}
diff --git a/packages/SettingsLib/DataStore/Android.bp b/packages/SettingsLib/DataStore/Android.bp
new file mode 100644
index 000000000000..868a4a50577e
--- /dev/null
+++ b/packages/SettingsLib/DataStore/Android.bp
@@ -0,0 +1,16 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibDataStore",
+ defaults: [
+ "SettingsLintDefaults",
+ ],
+ srcs: ["src/**/*"],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.collection_collection-ktx",
+ "guava",
+ ],
+}
diff --git a/packages/SettingsLib/DataStore/AndroidManifest.xml b/packages/SettingsLib/DataStore/AndroidManifest.xml
new file mode 100644
index 000000000000..fb446279e240
--- /dev/null
+++ b/packages/SettingsLib/DataStore/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.datastore">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreContext.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreContext.kt
new file mode 100644
index 000000000000..c6d6f772c5df
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreContext.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.backup.BackupAgent
+import android.app.backup.BackupDataOutput
+import android.app.backup.BackupHelper
+import android.os.Build
+import android.os.ParcelFileDescriptor
+import androidx.annotation.RequiresApi
+
+/**
+ * Context for backup.
+ *
+ * @see BackupHelper.performBackup
+ * @see BackupDataOutput
+ */
+class BackupContext
+internal constructor(
+ /**
+ * An open, read-only file descriptor pointing to the last backup state provided by the
+ * application. May be null, in which case no prior state is being provided and the application
+ * should perform a full backup.
+ *
+ * TODO: the state should support marshall/unmarshall for incremental back up.
+ */
+ val oldState: ParcelFileDescriptor?,
+
+ /** An open, read/write BackupDataOutput pointing to the backup data destination. */
+ private val data: BackupDataOutput,
+
+ /**
+ * An open, read/write file descriptor pointing to an empty file. The application should record
+ * the final backup.
+ */
+ val newState: ParcelFileDescriptor,
+) {
+ /**
+ * The quota in bytes for the application's current backup operation.
+ *
+ * @see [BackupDataOutput.getQuota]
+ */
+ val quota: Long
+ @RequiresApi(Build.VERSION_CODES.O) get() = data.quota
+
+ /**
+ * Additional information about the backup transport.
+ *
+ * See [BackupAgent] for supported flags.
+ *
+ * @see [BackupDataOutput.getTransportFlags]
+ */
+ val transportFlags: Int
+ @RequiresApi(Build.VERSION_CODES.P) get() = data.transportFlags
+}
+
+/** Context for restore. */
+class RestoreContext(val key: String)
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
new file mode 100644
index 000000000000..6a7ef5a35ac8
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.backup.BackupDataOutput
+import android.app.backup.BackupHelper
+import androidx.annotation.BinderThread
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+
+/** Entity for back up and restore. */
+interface BackupRestoreEntity {
+ /**
+ * Key of the entity.
+ *
+ * The key string must be unique within the data set. Note that it is invalid if the first
+ * character is \uFF00 or higher.
+ *
+ * @see BackupDataOutput.writeEntityHeader
+ */
+ val key: String
+
+ /**
+ * Backs up the entity.
+ *
+ * @param backupContext context for backup
+ * @param outputStream output stream to back up data
+ * @return false if backup file is deleted, otherwise true
+ */
+ @BinderThread
+ @Throws(IOException::class)
+ fun backup(backupContext: BackupContext, outputStream: OutputStream): EntityBackupResult
+
+ /**
+ * Restores the entity.
+ *
+ * @param restoreContext context for restore
+ * @param inputStream An open input stream from which the backup data can be read.
+ * @see BackupHelper.restoreEntity
+ */
+ @BinderThread
+ @Throws(IOException::class)
+ fun restore(restoreContext: RestoreContext, inputStream: InputStream)
+}
+
+/** Result of the backup operation. */
+enum class EntityBackupResult {
+ /** Update the entity. */
+ UPDATE,
+ /** Leave the entity intact. */
+ INTACT,
+ /** Delete the entity. */
+ DELETE,
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
new file mode 100644
index 000000000000..88d9dd6400ee
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.backup.BackupAgentHelper
+import android.app.backup.BackupDataInputStream
+import android.app.backup.BackupDataOutput
+import android.app.backup.BackupHelper
+import android.os.ParcelFileDescriptor
+import android.util.Log
+import com.google.common.io.ByteStreams
+import java.io.ByteArrayOutputStream
+import java.io.FilterInputStream
+import java.io.InputStream
+import java.io.OutputStream
+
+internal const val LOG_TAG = "BackupRestoreStorage"
+
+/**
+ * Storage with backup and restore support. Subclass must implement either [Observable] or
+ * [KeyedObservable] interface.
+ *
+ * The storage is identified by a unique string [name] and data set is split into entities
+ * ([BackupRestoreEntity]).
+ */
+abstract class BackupRestoreStorage : BackupHelper {
+ /**
+ * A unique string used to disambiguate the various storages within backup agent.
+ *
+ * It will be used as the `keyPrefix` of [BackupAgentHelper.addHelper].
+ */
+ abstract val name: String
+
+ private val entities: List<BackupRestoreEntity> by lazy { createBackupRestoreEntities() }
+
+ /** Entities to back up and restore. */
+ abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
+
+ override fun performBackup(
+ oldState: ParcelFileDescriptor?,
+ data: BackupDataOutput,
+ newState: ParcelFileDescriptor,
+ ) {
+ val backupContext = BackupContext(oldState, data, newState)
+ if (!enableBackup(backupContext)) {
+ Log.i(LOG_TAG, "[$name] Backup disabled")
+ return
+ }
+ Log.i(LOG_TAG, "[$name] Backup start")
+ for (entity in entities) {
+ val key = entity.key
+ val outputStream = ByteArrayOutputStream()
+ val result =
+ try {
+ entity.backup(backupContext, wrapBackupOutputStream(outputStream))
+ } catch (exception: Exception) {
+ Log.e(LOG_TAG, "[$name] Fail to backup entity $key", exception)
+ continue
+ }
+ when (result) {
+ EntityBackupResult.UPDATE -> {
+ val payload = outputStream.toByteArray()
+ val size = payload.size
+ data.writeEntityHeader(key, size)
+ data.writeEntityData(payload, size)
+ Log.i(LOG_TAG, "[$name] Backup entity $key: $size bytes")
+ }
+ EntityBackupResult.INTACT -> {
+ Log.i(LOG_TAG, "[$name] Backup entity $key intact")
+ }
+ EntityBackupResult.DELETE -> {
+ data.writeEntityHeader(key, -1)
+ Log.i(LOG_TAG, "[$name] Backup entity $key deleted")
+ }
+ }
+ }
+ Log.i(LOG_TAG, "[$name] Backup end")
+ }
+
+ /** Returns if backup is enabled. */
+ open fun enableBackup(backupContext: BackupContext): Boolean = true
+
+ fun wrapBackupOutputStream(outputStream: OutputStream): OutputStream {
+ return outputStream
+ }
+
+ override fun restoreEntity(data: BackupDataInputStream) {
+ val key = data.key
+ if (!enableRestore()) {
+ Log.i(LOG_TAG, "[$name] Restore disabled, ignore entity $key")
+ return
+ }
+ val entity = entities.firstOrNull { it.key == key }
+ if (entity == null) {
+ Log.w(LOG_TAG, "[$name] Cannot find handler for entity $key")
+ return
+ }
+ Log.i(LOG_TAG, "[$name] Restore $key: ${data.size()} bytes")
+ val restoreContext = RestoreContext(key)
+ try {
+ entity.restore(restoreContext, wrapRestoreInputStream(data))
+ } catch (exception: Exception) {
+ Log.e(LOG_TAG, "[$name] Fail to restore entity $key", exception)
+ }
+ }
+
+ /** Returns if restore is enabled. */
+ open fun enableRestore(): Boolean = true
+
+ fun wrapRestoreInputStream(inputStream: BackupDataInputStream): InputStream {
+ return LimitedNoCloseInputStream(inputStream)
+ }
+
+ override fun writeNewStateDescription(newState: ParcelFileDescriptor) {}
+}
+
+/**
+ * Wrapper of [BackupDataInputStream], limiting the number of bytes that can be read and make
+ * [close] no-op.
+ */
+class LimitedNoCloseInputStream(inputStream: BackupDataInputStream) :
+ FilterInputStream(ByteStreams.limit(inputStream, inputStream.size().toLong())) {
+ override fun close() {
+ // do not close original input stream
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
new file mode 100644
index 000000000000..221e2e89c33d
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.app.Application
+import android.app.backup.BackupAgentHelper
+import android.app.backup.BackupManager
+import android.content.Context
+import android.util.Log
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.ConcurrentHashMap
+
+/** Manager of [BackupRestoreStorage]. */
+class BackupRestoreStorageManager private constructor(private val application: Application) {
+ private val storages = ConcurrentHashMap<String, BackupRestoreStorage>()
+
+ private val executor = MoreExecutors.directExecutor()
+
+ private val observer = Observer { reason -> notifyBackupManager(null, reason) }
+
+ private val keyedObserver =
+ KeyedObserver<Any?> { key, reason -> notifyBackupManager(key, reason) }
+
+ private fun notifyBackupManager(key: Any?, reason: Int) {
+ // prefer not triggering backup immediately after restore
+ if (reason == ChangeReason.RESTORE) return
+ // TODO: log storage name
+ Log.d(LOG_TAG, "Notify BackupManager data changed for change: key=$key")
+ BackupManager.dataChanged(application.packageName)
+ }
+
+ /**
+ * Adds all the registered [BackupRestoreStorage] as the helpers of given [BackupAgentHelper].
+ *
+ * @see BackupAgentHelper.addHelper
+ */
+ fun addBackupAgentHelpers(backupAgentHelper: BackupAgentHelper) {
+ for ((keyPrefix, storage) in storages) {
+ backupAgentHelper.addHelper(keyPrefix, storage)
+ }
+ }
+
+ /**
+ * Callback when restore finished.
+ *
+ * The observers of the storages will be notified.
+ */
+ fun onRestoreFinished() {
+ for (storage in storages.values) {
+ storage.notifyRestoreFinished()
+ }
+ }
+
+ private fun BackupRestoreStorage.notifyRestoreFinished() {
+ when (this) {
+ is KeyedObservable<*> -> notifyChange(ChangeReason.RESTORE)
+ is Observable -> notifyChange(ChangeReason.RESTORE)
+ }
+ }
+
+ /**
+ * Adds a list of storages.
+ *
+ * The storage MUST implement [KeyedObservable] or [Observable].
+ */
+ fun add(vararg storages: BackupRestoreStorage) {
+ for (storage in storages) add(storage)
+ }
+
+ /**
+ * Adds a storage.
+ *
+ * The storage MUST implement [KeyedObservable] or [Observable].
+ */
+ fun add(storage: BackupRestoreStorage) {
+ val name = storage.name
+ val oldStorage = storages.put(name, storage)
+ if (oldStorage != null) {
+ throw IllegalStateException(
+ "Storage name '$name' conflicts:\n\told: $oldStorage\n\tnew: $storage"
+ )
+ }
+ storage.addObserver()
+ }
+
+ private fun BackupRestoreStorage.addObserver() {
+ when (this) {
+ is KeyedObservable<*> -> addObserver(keyedObserver, executor)
+ is Observable -> addObserver(observer, executor)
+ else ->
+ throw IllegalArgumentException(
+ "$this does not implement either KeyedObservable or Observable"
+ )
+ }
+ }
+
+ /** Removes all the storages. */
+ fun removeAll() {
+ for ((name, _) in storages) remove(name)
+ }
+
+ /** Removes storage with given name. */
+ fun remove(name: String): BackupRestoreStorage? {
+ val storage = storages.remove(name)
+ storage?.removeObserver()
+ return storage
+ }
+
+ private fun BackupRestoreStorage.removeObserver() {
+ when (this) {
+ is KeyedObservable<*> -> removeObserver(keyedObserver)
+ is Observable -> removeObserver(observer)
+ }
+ }
+
+ /** Returns storage with given name. */
+ fun get(name: String): BackupRestoreStorage? = storages[name]
+
+ /** Returns storage with given name, exception is raised if not found. */
+ fun getOrThrow(name: String): BackupRestoreStorage = storages[name]!!
+
+ companion object {
+ @Volatile private var instance: BackupRestoreStorageManager? = null
+
+ /** Returns the singleton of manager. */
+ @JvmStatic
+ fun getInstance(context: Context): BackupRestoreStorageManager {
+ val result = instance
+ if (result != null) return result
+ synchronized(this) {
+ if (instance == null) {
+ instance =
+ BackupRestoreStorageManager(context.applicationContext as Application)
+ }
+ }
+ return instance!!
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
new file mode 100644
index 000000000000..3ed4d46459cf
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import androidx.annotation.AnyThread
+import androidx.annotation.GuardedBy
+import androidx.collection.MutableScatterMap
+import java.util.WeakHashMap
+import java.util.concurrent.Executor
+
+/**
+ * Callback to be informed of changes in [KeyedObservable] object.
+ *
+ * The observer is weakly referenced, a strong reference must be kept.
+ */
+fun interface KeyedObserver<in K> {
+ /**
+ * Called by [KeyedObservable] in the event of changes.
+ *
+ * This callback will run in the given [Executor] when observer is added.
+ *
+ * @param key key that has been changed
+ * @param reason the reason of change
+ * @see KeyedObservable.addObserver
+ */
+ fun onKeyChanged(key: K, @ChangeReason reason: Int)
+}
+
+/**
+ * A key-value observable object allows to observe change with [KeyedObserver].
+ *
+ * Notes:
+ * - The order in which observers will be notified is unspecified.
+ * - The observer is weakly referenced to avoid memory leaking, the call site must keep a strong
+ * reference of the observer.
+ * - It is possible that the callback may be triggered even there is no real data change. For
+ * example, when data restore/clear happens, it might be too complex to check if data is really
+ * changed, thus all the registered observers are notified directly.
+ */
+@AnyThread
+interface KeyedObservable<K> {
+ /**
+ * Adds an observer for any key.
+ *
+ * The observer will be notified whenever a change happens. The [KeyedObserver.onKeyChanged]
+ * callback will be invoked with specific key that is modified. However, `null` key is passed in
+ * the cases that a bunch of keys are changed simultaneously (e.g. clear data, restore happens).
+ *
+ * @param observer observer to be notified
+ * @param executor executor to run the callback
+ */
+ fun addObserver(observer: KeyedObserver<K?>, executor: Executor)
+
+ /**
+ * Adds an observer on given key.
+ *
+ * The observer will be notified only when the given key is changed.
+ *
+ * @param key key to observe
+ * @param observer observer to be notified
+ * @param executor executor to run the callback
+ */
+ fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor)
+
+ /** Removes observer. */
+ fun removeObserver(observer: KeyedObserver<K?>)
+
+ /** Removes observer on given key. */
+ fun removeObserver(key: K, observer: KeyedObserver<K>)
+
+ /**
+ * Notifies all observers that a change occurs.
+ *
+ * All the any key and keyed observers are notified.
+ *
+ * @param reason reason of the change
+ */
+ fun notifyChange(@ChangeReason reason: Int)
+
+ /**
+ * Notifies observers that a change occurs on given key.
+ *
+ * The any key and specific key observers are notified.
+ *
+ * @param key key of the change
+ * @param reason reason of the change
+ */
+ fun notifyChange(key: K, @ChangeReason reason: Int)
+}
+
+/** A thread safe implementation of [KeyedObservable]. */
+class KeyedDataObservable<K> : KeyedObservable<K> {
+ // Instead of @GuardedBy("this"), guarded by itself because KeyedDataObservable object could be
+ // synchronized outside by the holder
+ @GuardedBy("itself") private val observers = WeakHashMap<KeyedObserver<K?>, Executor>()
+
+ @GuardedBy("itself")
+ private val keyedObservers = MutableScatterMap<K, WeakHashMap<KeyedObserver<K>, Executor>>()
+
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) {
+ val oldExecutor = synchronized(observers) { observers.put(observer, executor) }
+ if (oldExecutor != null && oldExecutor != executor) {
+ throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
+ }
+ }
+
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) {
+ val oldExecutor =
+ synchronized(keyedObservers) {
+ keyedObservers.getOrPut(key) { WeakHashMap() }.put(observer, executor)
+ }
+ if (oldExecutor != null && oldExecutor != executor) {
+ throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
+ }
+ }
+
+ override fun removeObserver(observer: KeyedObserver<K?>) {
+ synchronized(observers) { observers.remove(observer) }
+ }
+
+ override fun removeObserver(key: K, observer: KeyedObserver<K>) {
+ synchronized(keyedObservers) {
+ val observers = keyedObservers[key]
+ if (observers?.remove(observer) != null && observers.isEmpty()) {
+ keyedObservers.remove(key)
+ }
+ }
+ }
+
+ override fun notifyChange(@ChangeReason reason: Int) {
+ // make a copy to avoid potential ConcurrentModificationException
+ val observers = synchronized(observers) { observers.entries.toTypedArray() }
+ val keyedObservers = synchronized(keyedObservers) { keyedObservers.copy() }
+ for (entry in observers) {
+ val observer = entry.key // avoid reference "entry"
+ entry.value.execute { observer.onKeyChanged(null, reason) }
+ }
+ for (pair in keyedObservers) {
+ val key = pair.first
+ for (entry in pair.second) {
+ val observer = entry.key // avoid reference "entry"
+ entry.value.execute { observer.onKeyChanged(key, reason) }
+ }
+ }
+ }
+
+ private fun MutableScatterMap<K, WeakHashMap<KeyedObserver<K>, Executor>>.copy():
+ List<Pair<K, Array<Map.Entry<KeyedObserver<K>, Executor>>>> {
+ val result = ArrayList<Pair<K, Array<Map.Entry<KeyedObserver<K>, Executor>>>>(size)
+ forEach { key, value -> result.add(Pair(key, value.entries.toTypedArray())) }
+ return result
+ }
+
+ override fun notifyChange(key: K, @ChangeReason reason: Int) {
+ // make a copy to avoid potential ConcurrentModificationException
+ val observers = synchronized(observers) { observers.entries.toTypedArray() }
+ val keyedObservers =
+ synchronized(keyedObservers) { keyedObservers[key]?.entries?.toTypedArray() }
+ ?: arrayOf()
+ for (entry in observers) {
+ val observer = entry.key // avoid reference "entry"
+ entry.value.execute { observer.onKeyChanged(key, reason) }
+ }
+ for (entry in keyedObservers) {
+ val observer = entry.key // avoid reference "entry"
+ entry.value.execute { observer.onKeyChanged(key, reason) }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/ObservableBackupRestoreStorage.kt
index f80a90659545..0e399c01e763 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/CommunalSceneModule.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/ObservableBackupRestoreStorage.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.scene
+package com.android.settingslib.datastore
-import dagger.Module
-
-@Module interface CommunalSceneModule
+/**
+ * A [BackupRestoreStorage] that implements [Observable].
+ *
+ * This class provides the [Observable] implementations on top of [DataObservable] by delegation.
+ */
+abstract class ObservableBackupRestoreStorage :
+ BackupRestoreStorage(), Observable by DataObservable()
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
new file mode 100644
index 000000000000..6d0ca6690c9f
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import androidx.annotation.AnyThread
+import androidx.annotation.GuardedBy
+import androidx.annotation.IntDef
+import java.util.WeakHashMap
+import java.util.concurrent.Executor
+
+/** The reason of a change. */
+@IntDef(
+ ChangeReason.UNKNOWN,
+ ChangeReason.UPDATE,
+ ChangeReason.DELETE,
+ ChangeReason.RESTORE,
+ ChangeReason.SYNC_ACROSS_PROFILES,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class ChangeReason {
+ companion object {
+ /** Unknown reason of the change. */
+ const val UNKNOWN = 0
+ /** Data is updated. */
+ const val UPDATE = 1
+ /** Data is deleted. */
+ const val DELETE = 2
+ /** Data is restored from backup/restore framework. */
+ const val RESTORE = 3
+ /** Data is synced from another profile (e.g. personal profile to work profile). */
+ const val SYNC_ACROSS_PROFILES = 4
+ }
+}
+
+/**
+ * Callback to be informed of changes in [Observable] object.
+ *
+ * The observer is weakly referenced, a strong reference must be kept.
+ */
+fun interface Observer {
+ /**
+ * Called by [Observable] in the event of changes.
+ *
+ * This callback will run in the given [Executor] when observer is added.
+ *
+ * @param reason the reason of change
+ * @see [Observable.addObserver] for the notices.
+ */
+ fun onChanged(@ChangeReason reason: Int)
+}
+
+/** An observable object allows to observe change with [Observer]. */
+@AnyThread
+interface Observable {
+ /**
+ * Adds an observer.
+ *
+ * Notes:
+ * - The order in which observers will be notified is unspecified.
+ * - The observer is weakly referenced to avoid memory leaking, the call site must keep a strong
+ * reference of the observer.
+ * - It is possible that the callback may be triggered even there is no real data change. For
+ * example, when data restore/clear happens, it might be too complex to check if data is
+ * really changed, thus all the registered observers are notified directly.
+ *
+ * @param observer observer to be notified
+ * @param executor executor to run the [Observer.onChanged] callback
+ */
+ fun addObserver(observer: Observer, executor: Executor)
+
+ /** Removes given observer. */
+ fun removeObserver(observer: Observer)
+
+ /**
+ * Notifies observers that a change occurs.
+ *
+ * @param reason reason of the change
+ */
+ fun notifyChange(@ChangeReason reason: Int)
+}
+
+/** A thread safe implementation of [Observable]. */
+class DataObservable : Observable {
+ // Instead of @GuardedBy("this"), guarded by itself because DataObservable object could be
+ // synchronized outside by the holder
+ @GuardedBy("itself") private val observers = WeakHashMap<Observer, Executor>()
+
+ override fun addObserver(observer: Observer, executor: Executor) {
+ val oldExecutor = synchronized(observers) { observers.put(observer, executor) }
+ if (oldExecutor != null && oldExecutor != executor) {
+ throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
+ }
+ }
+
+ override fun removeObserver(observer: Observer) {
+ synchronized(observers) { observers.remove(observer) }
+ }
+
+ override fun notifyChange(@ChangeReason reason: Int) {
+ // make a copy to avoid potential ConcurrentModificationException
+ val entries = synchronized(observers) { observers.entries.toTypedArray() }
+ for (entry in entries) {
+ val observer = entry.key // avoid reference "entry"
+ entry.value.execute { observer.onChanged(reason) }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp
new file mode 100644
index 000000000000..8770dfa013d0
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/Android.bp
@@ -0,0 +1,24 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "SettingsLibDataStoreShell",
+ platform_apis: true,
+}
+
+android_robolectric_test {
+ name: "SettingsLibDataStoreTest",
+ srcs: ["src/**/*"],
+ static_libs: [
+ "SettingsLibDataStore",
+ "androidx.test.ext.junit",
+ "guava",
+ "mockito-robolectric-prebuilt", // mockito deps order matters!
+ "mockito-kotlin2",
+ ],
+ java_resource_dirs: ["config"],
+ instrumentation_for: "SettingsLibDataStoreShell",
+ coverage_libs: ["SettingsLibDataStore"],
+ upstream: true,
+}
diff --git a/packages/SettingsLib/DataStore/tests/AndroidManifest.xml b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..ffc24e4330d1
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.datastore.test">
+
+ <application android:debuggable="true" />
+</manifest>
diff --git a/packages/SettingsLib/DataStore/tests/config/robolectric.properties b/packages/SettingsLib/DataStore/tests/config/robolectric.properties
new file mode 100644
index 000000000000..fab7251d020b
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/config/robolectric.properties
@@ -0,0 +1 @@
+sdk=NEWEST_SDK
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
new file mode 100644
index 000000000000..bb791dc9a23c
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
+import org.junit.Assert.assertThrows
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class ObserverTest {
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var observer1: Observer
+
+ @Mock private lateinit var observer2: Observer
+
+ @Mock private lateinit var executor: Executor
+
+ private val observable = DataObservable()
+
+ @Test
+ fun addObserver_sameExecutor() {
+ observable.addObserver(observer1, executor)
+ observable.addObserver(observer1, executor)
+ }
+
+ @Test
+ fun addObserver_differentExecutor() {
+ observable.addObserver(observer1, executor)
+ assertThrows(IllegalStateException::class.java) {
+ observable.addObserver(observer1, MoreExecutors.directExecutor())
+ }
+ }
+
+ @Test
+ fun addObserver_weaklyReferenced() {
+ val counter = AtomicInteger()
+ var observer: Observer? = Observer { counter.incrementAndGet() }
+ observable.addObserver(observer!!, MoreExecutors.directExecutor())
+
+ observable.notifyChange(ChangeReason.UPDATE)
+ assertThat(counter.get()).isEqualTo(1)
+
+ // trigger GC, the observer callback should not be invoked
+ @Suppress("unused")
+ observer = null
+ System.gc()
+ System.runFinalization()
+
+ observable.notifyChange(ChangeReason.UPDATE)
+ assertThat(counter.get()).isEqualTo(1)
+ }
+
+ @Test
+ fun addObserver_notifyObservers_removeObserver() {
+ observable.addObserver(observer1, MoreExecutors.directExecutor())
+ observable.addObserver(observer2, executor)
+
+ observable.notifyChange(ChangeReason.DELETE)
+
+ verify(observer1).onChanged(ChangeReason.DELETE)
+ verify(observer2, never()).onChanged(any())
+ verify(executor).execute(any())
+
+ reset(observer1, executor)
+ observable.removeObserver(observer2)
+
+ observable.notifyChange(ChangeReason.UPDATE)
+ verify(observer1).onChanged(ChangeReason.UPDATE)
+ verify(executor, never()).execute(any())
+ }
+
+ @Test
+ fun notifyChange_addObserverWithinCallback() {
+ // ConcurrentModificationException is raised if it is not implemented correctly
+ observable.addObserver(
+ { observable.addObserver(observer1, executor) },
+ MoreExecutors.directExecutor()
+ )
+ observable.notifyChange(ChangeReason.UPDATE)
+ }
+}
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 5f5f1d59ac1c..5966c9f759fb 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -5,6 +5,7 @@ cipson@google.com
dsandler@android.com
edgarwang@google.com
evanlaird@google.com
+jiannan@google.com
juliacr@google.com
ykhung@google.com
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index bb7e8575ba1b..3acf075d8900 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -18,7 +18,6 @@ package com.android.settingslib.spa.widget.preference
import androidx.compose.foundation.clickable
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import com.android.settingslib.spa.framework.common.EntryMacro
@@ -107,14 +106,9 @@ fun Preference(
) {
val onClickWithLog = wrapOnClickWithLog(model.onClick)
val enabled = model.enabled()
- val modifier = remember(enabled) {
- if (onClickWithLog != null) {
- Modifier.clickable(
- enabled = enabled,
- onClick = onClickWithLog
- )
- } else Modifier
- }
+ val modifier = if (onClickWithLog != null) {
+ Modifier.clickable(enabled = enabled, onClick = onClickWithLog)
+ } else Modifier
EntryHighlight {
BasePreference(
title = model.title,
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 8d3582a736fe..cc23f6eedf9a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses is onderbreek om battery te beskerm"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Kontroleer tans laaibykomstigheid"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Gaan die laaibykomstigheid na"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor gegrond op jou gebruik"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> oor tot vol"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> oor tot vol"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses word geoptimeer"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laai tans"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index f6931ad30353..0ecd376626c4 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ባትሪን ለመጠበቅ ኃይል መሙላት በይቆይ ላይ"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - የኃይል መሙያ መለዋወጫዎችን በመፈተሽ ላይ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - የኃይል መሙላት መለዋወጫን ይፈትሹ"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ገደማ ቀርቷል"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ገደማ ቀርቷል"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"በአጠቃቀምዎ መሠረት <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ገደማ ቀርቷል"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g> ይቀራል"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት እንዲተባ ተደርጓል"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 92dc69ca54fe..1f313aef239c 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"‫<xliff:g id="LEVEL">%1$s</xliff:g> - الشحن معلَّق لحماية البطارية"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"‫<xliff:g id="LEVEL">%1$s</xliff:g> - يجب فحص ملحق الشحن"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - فحص ملحق الشحن"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا."</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا، بناءً على استخدامك"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"يتبقّى <xliff:g id="TIME">%1$s</xliff:g> حتى اكتمال شحن البطارية."</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقّى <xliff:g id="TIME">%2$s</xliff:g> حتى اكتمال شحن البطارية."</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - تم تحسين الشحن"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"‫<xliff:g id="LEVEL">%1$s</xliff:g>: جارٍ الشحن"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 48d3df4621f3..a4be8e997311 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেটাৰী সুৰক্ষিত কৰিবলৈ চাৰ্জিং স্থগিত ৰখা হৈছে"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিঙৰ আনুষংগিক বস্তু পৰীক্ষা কৰি থকা হৈছে"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিঙৰ সৈতে জড়িত আনুষংগিক সামগ্ৰী পৰীক্ষা কৰক"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি প্ৰায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ চাৰ্জ হৈ আছে"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index e27a40cdf7b1..df6dad208bd8 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyanı qorumaq üçün şarj gözlədilir"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarı yoxlanır"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarını yoxlayın"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"İstifadəyə əsasən təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tam şarj edilənədək <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj edilənədək <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj optimallaşdırılıb"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj edilir"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index dd48b70bd8ed..8bb8c838ed39 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju da bi se zaštitila baterija"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – provera dodatne opreme za punjenje"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Proverite dodatnu opremu za punjenje"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na osnovu korišćenja"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do kraja punjenja"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizovano"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Punjenje"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 48247a01034f..433c2430a40d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарадка прыпынена, каб абараніць акумулятар"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – правяраецца зарадная прылада"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>, праверце зарадную прыладу"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Зараду хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Зараду (<xliff:g id="LEVEL">%2$s</xliff:g>) хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Зараду пры такім выкарыстанні хопіць прыблізна на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – да поўнай зарадкі засталося: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарадка аптымізавана"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зараджаецца"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 035b1dd19c5a..27e27bef4f3a 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е поставено на пауза с цел запазване на батерията"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Аксесоарът за зареждане се проверява"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Проверете аксесоара за зареждане"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g> въз основа на използването"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оставащо време до пълно зареждане: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е оптимизирано"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарежда се"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 9a2f88c1eb1f..2cf055654f3b 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারিকে সুরক্ষিত রাখতে চার্জিং হোল্ড করা হয়েছে"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিংয়ের সরঞ্জাম চেক করা হচ্ছে"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অ্যাক্সেসরি চেক করুন"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ব্যবহারের উপর ভিত্তি করে আর আনুমানিক <xliff:g id="TIME_REMAINING">%1$s</xliff:g> চলবে"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জ করা হচ্ছে"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 01662dd8bed6..64ccf98c3aa8 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju radi zaštite baterije"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjera opreme za punjenje"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjerite opremu za punjenje"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na osnovu vaše potrošnje"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizirano"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 61898881baac..81f9e97a2203 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: la càrrega s\'ha posat en espera per protegir la bateria"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està comprovant l\'accessori de càrrega"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: revisa l\'accessori de càrrega"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: càrrega optimitzada"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està carregant"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index ec104b4a95e4..cb8c36b55580 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení je pozastaveno za účelem ochrany baterie"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Kontrola nabíjecího příslušenství"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Zkontrolujte nabíjecí příslušenství"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Při vašem obvyklém využití zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabití"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimalizované nabíjení"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index b92fc040cd30..625f05b57ac8 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Opladningen er sat på pause for at beskytte batteriet"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tjekker opladningstilbehøret"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tjek opladningstilbehøret"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage, alt efter hvordan du bruger enheden"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fuldt opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – fuldt opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladning er optimeret"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – oplades"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index debdb1e6b361..f622f61505f5 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladevorgang zum Schutz des Akkus angehalten"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladezubehör wird geprüft"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladezubehör prüfen"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Voll in <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laden wird optimiert"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Wird geladen"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 6cf05597feb2..861f4722edfb 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Η φόρτιση τέθηκε σε αναμονή για προστασία της μπαταρίας"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Έλεγχος αξεσουάρ φόρτισης"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Έλεγχος αξεσουάρ φόρτισης"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g>, βάσει της χρήσης σας"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για πλήρη φόρτιση"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένουν <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η φόρτιση βελτιστοποιήθηκε"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Φόρτιση"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b2c5718c999f..e6923de1af8c 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Checking charging accessory"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – check charging accessory"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 21fff5e1e3be..36dfd2fb0c28 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging on hold to protect battery"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Checking charging accessory"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Check charging accessory"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b2c5718c999f..e6923de1af8c 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Checking charging accessory"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – check charging accessory"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b2c5718c999f..e6923de1af8c 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Checking charging accessory"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – check charging accessory"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 25e2779c4f5c..8b05e12f2ee4 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎Overridden by ‎‏‎‎‏‏‎<xliff:g id="TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME_STRING">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging on hold to protect battery‎‏‎‎‏‎"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Checking charging accessory‎‏‎‎‏‎"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Check charging accessory‎‏‎‎‏‎"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎About ‎‏‎‎‏‏‎<xliff:g id="TIME_REMAINING">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left‎‏‎‎‏‎"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎About ‎‏‎‎‏‏‎<xliff:g id="TIME_REMAINING">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎About ‎‏‎‎‏‏‎<xliff:g id="TIME_REMAINING">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left based on your usage‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index d0ba34c4c8f4..a6fdf569954c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Se detuvo la carga para proteger la batería"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Verificando el accesorio de carga"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Verifica el accesorio de carga"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g> en función de tu uso"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Cargando"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 77049e0c6837..d57a33a305c6 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga pausada para proteger la batería"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Comprobando accesorio de carga"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Comprueba el accesorio de carga"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tiempo restante aproximado según tu uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> hasta la carga completa"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hasta la carga completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Cargar"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 6a3c208efa59..dfe78d8f3557 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on aku kaitsmiseks ootele pandud"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimistarviku kontrollimine"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – kontrollige laadimistarvikut"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Teie kasutuse põhjal on jäänud ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Täislaadimiseks kulub <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – täislaadimiseks kulub <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on optimeeritud"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 64e372ac182a..3c6309dad77f 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: kargatze-prozesua zain dago bateria babesteko"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>: kargatzeko osagarria egiaztatzen"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Eman begiratu bat kargatzeko osagarriari"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Erabilera kontuan izanda, <xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kargatzeko modu optimizatua"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Kargatzen"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 01d4618a319e..2cb1873e5fbe 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - برای محافظت از باتری، شارژ موقتاً متوقف شده است"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - بررسی لوازم شارژ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - لوازم شارژ را بررسی کنید"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"براساس مصرفتان، تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ بهینه شده است"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - درحال شارژ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 529bed536f1f..29459ca5ee1f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus on keskeytetty akun suojaamiseksi"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tarkistetaan latauslisävarustetta"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tarkista latauslisävaruste"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Noin <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä käyttösi perusteella"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus optimoitu"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladataan"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index ea2383425dc9..f97b36ea895d 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -257,7 +257,7 @@
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Association de l\'appareil en cours…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Échec de l\'association de l\'appareil Soit le code QR est incorrect, soit l\'appareil n\'est pas connecté au même réseau."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresse IP et port"</string>
- <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Numériser le code QR"</string>
+ <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Balayer le code QR"</string>
<string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associer l\'appareil par Wi-Fi en numérisant un code QR"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Veuillez vous connecter à un réseau Wi-Fi"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, débogage, développeur"</string>
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> : <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – La recharge a été mise en pause pour protéger la pile"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Vérification de l\'accessoire de recharge en cours…"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Vérifier l\'accessoire de recharge"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> en fonction de votre usage"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la recharge complète"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la recharge complète)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Recharge en cours…"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index dbf107acf46e..7f7ba637b47d 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge en pause pour protéger la batterie"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Vérification de l\'accessoire de recharge"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> : vérifiez l\'accessoire de recharge"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Chargée à 100 %% dans <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - En charge"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 233c9d4d6a1b..179d946c23da 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>. A carga púxose en pausa para protexer a batería"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>. Comprobando accesorio de carga"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>. Comproba o accesorio de carga"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Tempo restante aproximado (<xliff:g id="LEVEL">%2$s</xliff:g>): <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo restante aproximado en función do uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (cargando)"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 762ed28a4317..cafe86c26b77 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીને સુરક્ષિત રાખવા માટે, ચાર્જિંગ હોલ્ડ પર રાખવામાં આવ્યું છે"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઍક્સેસરી ચેક કરી રહ્યાં છીએ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઍક્સેસરી ચેક કરો"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"તમારા વપરાશના આધારે લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6a0e3e0f706f..53cf7247b996 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - बैटरी को सुरक्षित रखने के लिए, फ़ोन को चार्ज होने से रोक दिया गया है"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऐक्सेसरी की जांच की जा रही है"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऐक्सेसरी की जांच करें"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"आपके इस्तेमाल के हिसाब से बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज हो रही है"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f019936ed9c4..f06baad6ea6c 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je pauzirano radi zaštite baterije"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjera dodatka za punjenje"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjerite dodatak za punjenje"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Još otprilike <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Još otprilike <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na temelju vaše upotrebe"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do napunjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d3d2960b22b9..39d905f0bf46 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Az akkumulátor védelme érdekében a töltés szünetel"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akkumulátortartozék ellenőrzése…"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ellenőrizze a töltőtartozékot"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Körülbelül <xliff:g id="TIME_REMAINING">%1$s</xliff:g> maradt hátra az eszköz használata alapján"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> a teljes töltöttségig"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttségig"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimalizált töltés"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Töltés…"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 4c14ae12fbb1..55f09e297f2a 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու համար"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորման սարքը ստուգվում է"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ստուգեք լիցքավորիչը"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>՝ կախված օգտագործման եղանակից"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումն օպտիմալացված է"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — Լիցքավորում"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 1379ecfc99b7..885729da1b66 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dihentikan sementara untuk melindungi baterai"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Memeriksa aksesori pengisi daya"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Periksa aksesori pengisian daya"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi berdasarkan penggunaan Anda"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sampai penuh"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sampai penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dioptimalkan"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengisi daya"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index e48d063d9d3c..020191b55e7c 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla í bið til að vernda rafhlöðuna"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Athugar hleðslutæki"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Athugaðu hleðslubúnaðinn"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir miðað við notkun þína"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla fínstillt"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Í hleðslu"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b33551440684..37a20675abe7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica in sospeso per proteggere la batteria"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Controllo dell\'accessorio di ricarica"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Controlla l\'accessorio di ricarica"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo rimanente in base al tuo utilizzo: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> alla ricarica completa"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla ricarica completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica ottimizzata"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ In carica"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Sconosciuta"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"In carica"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index a559cc616105..e3c1f65b4a4d 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -457,7 +457,8 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה הושהתה כדי להגן על הסוללה"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – מתבצעת בדיקה של אביזר הטעינה"</string>
+ <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) -->
+ <skip />
<string name="power_remaining_duration_only" msgid="8264199158671531431">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"הזמן הנותר על סמך השימוש שלך: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +479,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה עברה אופטימיזציה"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – בטעינה"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index a7be818b3f85..d87d0f8dafbc 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g>によって上書き済み"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - バッテリーを保護するため、充電を一時停止しています"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電用アクセサリを確認しています"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電用アクセサリを確認してください"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"残り時間: 約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"残り時間: 約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"残り時間: 約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(使用状況に基づく)"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 完了まであと <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電が最適化されています"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index eb06df31e676..7812f0b21601 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – დატენვა შეჩერებულია ბატარეის დასაცავად"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – მიმდინარეობს დამტენი აქსესუარის შემოწმება"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: მიმდინარეობს დამტენი აქსესუარის შემოწმება"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"დარჩა დაახლოებით <xliff:g id="TIME_REMAINING">%1$s</xliff:g>, ბატარეის მოხმარების გათვალისწინებით"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - დატენვა ოპტიმიზირებულია"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – იტენება"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"უცნობი"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"იტენება"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 86da0a4c0179..300d4e517013 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: батареяны қорғау үшін зарядтау кідіртіледі."</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>: зарядтау құрылғысы тексеріледі."</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау құрылғысын тексеріңіз"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Пайдалану деректеріңізге сәйкес енді шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Толық зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды."</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: толық зарядталуға <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарядталып жатыр"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядтау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 848e9049df33..373cfc656789 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"បដិសេធ​ដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងផ្អាកការសាកថ្ម ដើម្បីការពារថ្ម"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងពិនិត្យមើលគ្រឿងសាកថ្ម"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ពិនិត្យមើលគ្រឿងសាកថ្ម"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"នៅសល់​ប្រហែល <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ទៀត"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"នៅសល់​ប្រហែល <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ទៀត (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"នៅសល់​ប្រហែល <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ទៀត ផ្អែក​លើការ​ប្រើប្រាស់​របស់អ្នក"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានបង្កើនប្រសិទ្ធភាពនៃការសាក"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងសាកថ្ម"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"មិន​ស្គាល់"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងសាក​ថ្ម"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index d13019c28fe5..bf84cc48aa39 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯನ್ನು ರಕ್ಷಿಸಲು ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಹೋಲ್ಡ್‌ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಪರಿಕರವನ್ನು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಆ್ಯಕ್ಸೆಸರಿಯನ್ನು ಪರಿಶೀಲಿಸಿ"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"(<xliff:g id="LEVEL">%2$s</xliff:g>) ತಲುಪಲು <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ನಿಮ್ಮ ಬಳಕೆಯ ಆಧಾರದ ಮೇಲೆ ಸುಮಾರು <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> - ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 3c8ad01f0f02..b184ef43e5b7 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>, <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 배터리 보호를 위해 충전 일시중지"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 액세서리 확인 중"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 액세서리 확인"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"남은 시간: 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"남은 시간 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"내 사용량을 기준으로 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> 남음"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> 후 충전 완료"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 최적화됨"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 충전 중"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index cec61bbdf764..e565e444536c 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны коргоо үчүн кубаттоо тындырылды"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубаттоо шайманы текшерилүүдө"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубаттоо шайманын текшериңиз"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Колдонгонуңузга караганда болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталат"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталат"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — Кубаттоо жакшыртылды"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубатталууда"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 420925920982..6caaf952b364 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຢຸດການສາກຊົ່ວຄາວເພື່ອປົກປ້ອງແບັດເຕີຣີ"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ກຳລັງກວດສອບອຸປະກອນເສີມສຳລັບການສາກ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ກວດສອບອຸປະກອນເສີມສຳລັບການສາກ"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ເຫຼືອອີກປະມານ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ອ້າງອີງຈາກການນຳໃຊ້ຂອງທ່ານ"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ຍັງເຫຼືອອີກ <xliff:g id="TIME">%1$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"ຍັງເຫຼືອອີກ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ການສາກຖືກປັບໃຫ້ເໝາະສົມແລ້ວ"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ກຳລັງສາກໄຟ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index cb0069d66e42..bc66a806375f 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas pristabdytas, siekiant apsaugoti akumuliatorių"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – tikrinamas įkrovimo priedas"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – patikrinkite įkrovimo priedą"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g>, atsižvelgiant į naudojimą"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Liko <xliff:g id="TIME">%1$s</xliff:g>, kol bus visiškai įkrauta"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko <xliff:g id="TIME">%2$s</xliff:g>, kol bus visiškai įkrauta"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas optimizuotas"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkraunama"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index db566881f016..8aac7f1e9609 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -457,7 +457,8 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> — <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde apturēta, lai aizsargātu akumulatoru"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> — notiek uzlādes piederuma pārbaude"</string>
+ <!-- no translation found for power_incompatible_charging_settings_home_page (1322050766135126880) -->
+ <skip />
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Aptuvenais atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Aptuvenais atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ņemot vērā lietojumu, atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +479,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde optimizēta"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — notiek uzlāde"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 6738e2068ae1..70bcc2100ddd 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - полнењето е паузирано за да се заштити батеријата"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - се проверува додатокот за полнење"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Проверете го додатокот за полнење"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> според вашето користење"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полна батерија"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полна батерија"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Полнењето е оптимизирано"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ се полни"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index a5c38bc22d1f..9a297e5fb4d8 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ബാറ്ററി പരിരക്ഷിക്കാൻ ചാർജിംഗ് ഹോൾഡിലാണ്"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ആക്സസറി പരിശോധിക്കുന്നു"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ആക്‌സസറി പരിശോധിക്കുക"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏതാണ്ട് <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"പൂർണ്ണമാകാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമാകാൻ <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ചാർജ് ചെയ്യുന്നു"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index c03c89f68b18..000e306afef3 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг хамгаалахын тулд цэнэглэхийг хүлээлгэсэн"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх нэмэлт хэрэгслийг шалгаж байна"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх нэмэлт хэрэгслийг шалгах"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Таны хэрэглээнд үндэслэн ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Дүүрэх хүртэл <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - дүүрэх хүртэл <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх явцыг оновчилсон"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэж байна"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index bbe782da8f85..6af0fbdc0ac8 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरीचे संरक्षण करण्यासाठी चार्जिंग थांबवले आहे"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंगसंबंधित ॲक्सेसरी तपासत आहे"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंगसंबंधित ॲक्सेसरी तपासा"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"तुमच्‍या वापरावर आधारित अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%1$s</xliff:g> शिल्लक आहेत"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%2$s</xliff:g> शिल्लक आहे"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऑप्टिमाइझ केले"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ चार्ज होत आहे"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 3da4f08be11b..621b4694026b 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan ditunda untuk melindungi bateri"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Memeriksa aksesori pengecasan"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Periksa aksesori pengecasan"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi berdasarkan penggunaan anda"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sebelum penuh"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sebelum penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan dioptimumkan"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengecas"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f4b669773aeb..60161c344ebb 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီကာကွယ်ရန် အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းပစ္စည်း စစ်ဆေးနေသည်"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းပစ္စည်း စစ်ရန်"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"သင်၏ အသုံးပြုမှု အပေါ် မူတည်၍ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုသည်"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> လိုသည်"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းနေသည်"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index fbdc9e33f576..256a71bb57c4 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er satt på vent for å beskytte batteriet"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Sjekker ladetilbehøret"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Sjekk ladetilbehøret"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen basert på bruken din"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fulladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er optimalisert"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – lader"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 245a31f9fcae..fdd965a6e52a 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ब्याट्री जोगाउन चार्जिङ होल्ड गरिएको छ"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिङ एक्सेसरीको जाँच गरिँदै छ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिङ एक्सेसरी जाँच्नुहोस्"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"तपाईंको प्रयोगको आधारमा लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूरा चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गरिँदै छ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै छ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 4eafb5740e05..72e57af602e8 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: opladen is in de wacht gezet om de batterij te beschermen"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>: oplaadaccessoire checken"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Oplaadaccessoire checken"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Nog ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Nog ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Nog ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> op basis van je gebruik"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Vol over <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - vol over <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Opladen geoptimaliseerd"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Opladen"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 5a8bed43faf7..723af105f8ea 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍‌ରାଇଡ୍‌ କରାଯାଇଛି"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବେଟେରୀକୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂ ଆକସେସୋରୀକୁ ଯାଞ୍ଚ କରାଯାଉଛି"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂ ଆକସେସୋରୀକୁ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ପାଇଁ (<xliff:g id="LEVEL">%2$s</xliff:g>) ବଳକା ଅଛି"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ଆପଣଙ୍କ ବ୍ୟବହାରକୁ ଆଧାର କରି ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%2$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ଚାର୍ଜିଂ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index df1fea17f645..8a77c12f6c0c 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਐਕਸੈਸਰੀ ਦੀ ਜਾਂਚ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਐਕਸੈਸਰੀ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index e9f9da8e095b..375a35f6edef 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – wstrzymano ładowanie, aby chronić baterię"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – sprawdzam akcesoria do ładowania"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – sprawdź akcesoria do ładowania"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (na podstawie Twojego sposobu korzystania)"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zoptymalizowane"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 4ee1cbd417ef..55b9db509200 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento suspenso para proteger a bateria"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Verificando o acessório de carregamento"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: verifique o acessório de carregamento"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo restante aproximado, com base no seu uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index eae5cc965f7f..ca704e7bbb87 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento em espera para proteger a bateria"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – A verificar o acessório de carregamento"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Verificar acessório de carregamento"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Resta(m) cerca de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> com base na sua utilização"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até à carga máxima"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até à carga máxima"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: carregamento otimizado"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – A carregar"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"A carregar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 4ee1cbd417ef..55b9db509200 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento suspenso para proteger a bateria"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Verificando o acessório de carregamento"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>: verifique o acessório de carregamento"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo restante aproximado, com base no seu uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 91261d49f15e..fa89c1a4c3d8 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcarea s-a întrerupt pentru a proteja bateria"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Se verifică accesoriul de încărcare"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Verifică accesoriul de încărcare"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"În baza utilizării, timpul rămas este de aproximativ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> până la finalizare"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la finalizare"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcare optimizată"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Se încarcă"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 87b2a938fa31..d85307098a35 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"Уровень заряда – <xliff:g id="PERCENTAGE">%1$s</xliff:g>. <xliff:g id="TIME_STRING">%2$s</xliff:g>."</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>, зарядка приостановлена для защиты батареи"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>, проверяется зарядное устройство"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g>, проверьте зарядное устройство"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Заряда (<xliff:g id="LEVEL">%2$s</xliff:g>) хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g> при текущем уровне расхода"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядка оптимизирована"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряжается"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 72bd507a5175..093f216afccc 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය ආරක්ෂා කිරීම සඳහා ආරෝපණය රඳවා තබා ඇත"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණ ආයිත්තම පරීක්ෂා කිරීම"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණ ආයිත්තම පරීක්ෂා කරන්න"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ක් පමණ ඉතිරියි"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ක් පමණ ඉතිරියි (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ඔබේ භාවිතය මත පදනම්ව <xliff:g id="TIME_REMAINING">%1$s</xliff:g> පමණ ඉතිරිව ඇත"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"සම්පූර්ණ වීමට <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - සම්පූර්ණ වීමට <xliff:g id="TIME">%2$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය ප්‍රශස්ත කර ඇත"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වේ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්‍ර ආරෝපණය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index dd8be3a1d7a0..1c8293312871 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíjanie je pozastavené, aby sa chránila batéria"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – kontroluje sa nabíjacie príslušenstvo"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – skontrolujte nabíjacie príslušenstvo"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ešte približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> – závisí to od intenzity využitia"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabitia"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nabíjanie je optimalizované"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíja sa"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index eefb119eca02..f97dd78f8179 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Zaradi zaščite baterije je polnjenje na čakanju"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Preverjanje pripomočka za polnjenje"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Preverite pripomoček za polnjenje"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Glede na način uporabe še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Še <xliff:g id="TIME">%1$s</xliff:g> do napolnjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – še <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje je optimizirano"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e847c515bb7e..a802f115da4a 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi është vendosur në pritje për të mbrojtur baterinë"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Po kontrollohet aksesori i karikimit"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kontrollo aksesorin e karikimit"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura bazuar në përdorimin tënd"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi u optimizua"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Po karikohet"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index ed0e9a6ea975..df2215911673 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је на чекању да би се заштитила батерија"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – провера додатне опреме за пуњење"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Проверите додатну опрему за пуњење"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g> на основу коришћења"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до краја пуњења"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до краја пуњења"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је оптимизовано"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Пуњење"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index b4de50408daa..79b8399527a0 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har pausats för att skydda batteriet"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kontrollerar laddningstillbehöret"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Kontrollera laddningstillbehöret"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar utifrån din användning"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har optimerats"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laddas"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 40725c92d6ea..c0ace5805e63 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Imetanguliwa na <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Imesitisha kuchaji ili kulinda betri yako"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Inakagua kifaa cha kuchaji"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kagua kifaa cha kuchaji"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Zimesalia takribani <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Zimesalia takribani <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Zimesalia takribani <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kulingana na jinsi unavyoitumia"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> zimesalia ijae chaji"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hali ya kuchaji imeboreshwa"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Inachaji"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 2fe243aee324..ebca26dc3063 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - பேட்டரியைப் பாதுகாப்பதற்காகச் சார்ஜிங் இடைநிறுத்தப்பட்டுள்ளது"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் துணைக்கருவியைச் சரிபார்க்கிறது"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் துணைக்கருவியைச் சரிபாருங்கள்"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"உபயோகத்தின் அடிப்படையில் கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"முழுவதும் சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழுவதும் சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் மேம்படுத்தப்பட்டது"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ சார்ஜாகிறது"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 43f2b835bc44..cf3c08acc54c 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - బ్యాటరీని రక్షించడానికి ఛార్జింగ్ హోల్డ్‌లో ఉంచబడింది"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ చేసే పరికరాన్ని చెక్ చేయండి"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ యాక్సెసరీని ఎంచుకోండి"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"దాదాపు <xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"మీ వినియోగం ఆధారంగా దాదాపు <xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవుతోంది"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 8395a5185363..a74265ee3e9a 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"แทนที่โดย <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - หยุดการชาร์จชั่วคราวเพื่อถนอมแบตเตอรี่"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - กำลังตรวจสอบอุปกรณ์เสริมสำหรับการชาร์จ"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - ตรวจสอบอุปกรณ์เสริมสำหรับการชาร์จ"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"เหลืออีกประมาณ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"เหลืออีกประมาณ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"เหลืออีกประมาณ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ขึ้นอยู่กับการใช้งานของคุณ"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g>จึงจะเต็ม"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ปรับการชาร์จให้เหมาะสมแล้ว"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ กำลังชาร์จ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a337bd8a69f2..18d3d3b626fa 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-hold ang pag-charge para protektahan ang baterya"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Sinusuri ang accessory sa pag-charge"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Suriin ang accessory sa pag-charge"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira batay sa iyong paggamit"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> na lang bago mapuno"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> na lang bago mapuno"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-optimize ang pag-charge"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nagcha-charge"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 43e18edca594..c4241150ad12 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pili korumak için şarj beklemede"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarı kontrol ediliyor"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarını kontrol edin"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Kullanımınıza dayalı olarak yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tamamen şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 984bb7978294..97592f970b67 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання призупинено, щоб захистити акумулятор"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – перевірка зарядного пристрою"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – перевірте зарядний пристрій"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Згідно з даними про використання залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання оптимізовано"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджається"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index bc8a46748d8b..9d3405234e5b 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - بیٹری کی حفاظت کرنے کے لیے چارجنگ ہولڈ پر ہے"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ ایکسیسری کی جانچ کی جا رہی ہے"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ ایکسیسری چیک کریں"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"مکمل چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"مکمل چارج ہونے میں <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ کو بہتر بنایا گیا"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارج ہو رہی ہے"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index a655dc0f0304..bd38bb44df6e 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyani himoyalash uchun quvvatlash toʻxtatildi"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash aksessuari tekshirilmoqda"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quvvatlash aksessuarini tekshiring"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Quvvati tugashiga taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Toʻlishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Toʻlishiga <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash optimallashtirildi"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlanmoqda"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 643f8ca85499..5b91df3e44a2 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang tạm ngưng sạc để bảo vệ pin"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang kiểm tra phụ kiện sạc"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hãy kiểm tra phụ kiện sạc"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> nữa là pin đầy"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là pin đầy"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang sạc"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 3b08e1f27bd6..594922a4050d 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 为保护电池,已暂停充电"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在检查充电配件"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 请检查充电配件"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根据您的使用情况,大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电方式已优化"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在充电"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index eba782e54e48..b3aafae4a3c6 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在檢查充電配件"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - 檢查充電配件"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根據你的使用情況,還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充滿電"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充滿電"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 已優化充電"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 充電中"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 099ad0364b0e..c3be21a93d8c 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在檢查充電配件"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> · 請檢查充電配件"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"目前電量為 <xliff:g id="LEVEL">%2$s</xliff:g>,還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根據你的使用情形,還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充飽"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電效能已最佳化"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 4d652f819ff3..f6aaf813af84 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -457,7 +457,7 @@
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kumisiwe ukuze kuvikelwe ibhethri"</string>
- <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kuhlolwa okokushaja"</string>
+ <string name="power_incompatible_charging_settings_home_page" msgid="1322050766135126880">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hlola insiza yokushaja"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Cishe u-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele ngokususelwe ekusebenziseni wakho"</string>
@@ -478,8 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> okusele kuze kugcwale"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> okusele kuze kugcwale"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kuthuthukisiwe"</string>
- <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
- <skip />
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"Iku-<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Iyashaja"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 61c3ce7f6988..c2c82b35317b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1764,40 +1764,4 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
boolean getUnpairing() {
return mUnpairing;
}
-
- ListenableFuture<Void> syncProfileForMemberDevice() {
- return ThreadUtils.getBackgroundExecutor()
- .submit(
- () -> {
- List<Pair<LocalBluetoothProfile, Boolean>> toSync =
- Stream.of(
- mProfileManager.getA2dpProfile(),
- mProfileManager.getHeadsetProfile(),
- mProfileManager.getHearingAidProfile(),
- mProfileManager.getLeAudioProfile(),
- mProfileManager.getLeAudioBroadcastAssistantProfile())
- .filter(Objects::nonNull)
- .map(profile -> new Pair<>(profile, profile.isEnabled(mDevice)))
- .toList();
-
- for (var t : toSync) {
- LocalBluetoothProfile profile = t.first;
- boolean enabledForMain = t.second;
-
- for (var member : mMemberDevices) {
- BluetoothDevice btDevice = member.getDevice();
-
- if (enabledForMain != profile.isEnabled(btDevice)) {
- Log.d(TAG, "Syncing profile " + profile + " to "
- + enabledForMain + " for member device "
- + btDevice.getAnonymizedAddress() + " of main device "
- + mDevice.getAnonymizedAddress());
- profile.setEnabled(btDevice, enabledForMain);
- }
- }
- }
- return null;
- }
- );
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 32eec7e709af..4e52c77f27b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -363,7 +363,6 @@ public class CachedBluetoothDeviceManager {
if (profileId == BluetoothProfile.HEADSET
|| profileId == BluetoothProfile.A2DP
|| profileId == BluetoothProfile.LE_AUDIO
- || profileId == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
|| profileId == BluetoothProfile.CSIP_SET_COORDINATOR) {
return mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
state);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index e67ec48d3401..a49314aae1b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -379,7 +379,6 @@ public class CsipDeviceManager {
if (hasChanged) {
log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: "
+ mCachedDevices);
- preferredMainDevice.syncProfileForMemberDevice();
}
return hasChanged;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index ca47efdc5df3..1069b715d946 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -346,11 +346,15 @@ public class HearingAidDeviceManager {
} else {
long hiSyncId = asha.getHiSyncId(cachedDevice.getDevice());
if (isValidHiSyncId(hiSyncId)) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ final HearingAidInfo info = new HearingAidInfo.Builder()
.setAshaDeviceSide(asha.getDeviceSide(cachedDevice.getDevice()))
.setAshaDeviceMode(asha.getDeviceMode(cachedDevice.getDevice()))
- .setHiSyncId(hiSyncId);
- return infoBuilder.build();
+ .setHiSyncId(hiSyncId)
+ .build();
+ if (DEBUG) {
+ Log.d(TAG, "generateHearingAidInfo, " + cachedDevice + ", info=" + info);
+ }
+ return info;
}
}
@@ -358,15 +362,20 @@ public class HearingAidDeviceManager {
final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
if (hapClientProfile == null || leAudioProfile == null) {
Log.w(TAG, "HapClientProfile or LeAudioProfile is not supported on this device");
- } else {
+ } else if (cachedDevice.getProfiles().stream().anyMatch(
+ p -> p instanceof HapClientProfile)) {
int audioLocation = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
int hearingAidType = hapClientProfile.getHearingAidType(cachedDevice.getDevice());
if (audioLocation != BluetoothLeAudio.AUDIO_LOCATION_INVALID
&& hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ final HearingAidInfo info = new HearingAidInfo.Builder()
.setLeAudioLocation(audioLocation)
- .setHapDeviceType(hearingAidType);
- return infoBuilder.build();
+ .setHapDeviceType(hearingAidType)
+ .build();
+ if (DEBUG) {
+ Log.d(TAG, "generateHearingAidInfo, " + cachedDevice + ", info=" + info);
+ }
+ return info;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index bdb58719b1a8..5f026c451152 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -74,6 +74,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -84,6 +85,7 @@ public abstract class InfoMediaManager extends MediaManager {
private static final String TAG = "InfoMediaManager";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
/** Checked exception that signals the specified package is not present in the system. */
public static class PackageNotAvailableException extends Exception {
@@ -227,6 +229,16 @@ public abstract class InfoMediaManager extends MediaManager {
Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap);
}
+ protected final MediaDevice findMediaDevice(@NonNull String id) {
+ for (MediaDevice mediaDevice : mMediaDevices) {
+ if (mediaDevice.getId().equals(id)) {
+ return mediaDevice;
+ }
+ }
+ Log.e(TAG, "findMediaDevice() can't find device with id: " + id);
+ return null;
+ }
+
/**
* Get current device that played media.
* @return MediaDevice
@@ -433,7 +445,7 @@ public abstract class InfoMediaManager extends MediaManager {
protected final synchronized void refreshDevices() {
rebuildDeviceList();
- dispatchDeviceListAdded();
+ dispatchDeviceListAdded(mMediaDevices);
}
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
index 8bebd6ee3448..d562c8a82f2d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
@@ -15,9 +15,9 @@
*/
package com.android.settingslib.media;
+import android.annotation.NonNull;
import android.app.Notification;
import android.content.Context;
-import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
@@ -29,10 +29,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public abstract class MediaManager {
- private static final String TAG = "MediaManager";
-
protected final Collection<MediaDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
- protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
protected Context mContext;
protected Notification mNotification;
@@ -54,19 +51,9 @@ public abstract class MediaManager {
}
}
- protected MediaDevice findMediaDevice(String id) {
- for (MediaDevice mediaDevice : mMediaDevices) {
- if (mediaDevice.getId().equals(id)) {
- return mediaDevice;
- }
- }
- Log.e(TAG, "findMediaDevice() can't found device");
- return null;
- }
-
- protected void dispatchDeviceListAdded() {
+ protected void dispatchDeviceListAdded(@NonNull List<MediaDevice> devices) {
for (MediaDeviceCallback callback : getCallbacks()) {
- callback.onDeviceListAdded(new ArrayList<>(mMediaDevices));
+ callback.onDeviceListAdded(new ArrayList<>(devices));
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
deleted file mode 100644
index 69f83a4dfa3c..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.wifi;
-
-import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
-import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.getMaxNetworkSelectionDisableReason;
-
-import static com.android.settingslib.flags.Flags.newStatusBarIcons;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.icu.text.MessageFormat;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.settingslib.R;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-public class WifiUtils {
-
- private static final String TAG = "WifiUtils";
-
- private static final int INVALID_RSSI = -127;
-
- /**
- * The intent action shows Wi-Fi dialog to connect Wi-Fi network.
- * <p>
- * Input: The calling package should put the chosen
- * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
- * the {@link #EXTRA_CHOSEN_WIFI_ENTRY_KEY}.
- * <p>
- * Output: Nothing.
- */
- @VisibleForTesting
- static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
-
- /**
- * Specify a key that indicates the WifiEntry to be configured.
- */
- @VisibleForTesting
- static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
-
- /**
- * The lookup key for a boolean that indicates whether a chosen WifiEntry request to connect to.
- * {@code true} means a chosen WifiEntry request to connect to.
- */
- @VisibleForTesting
- static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
-
- /**
- * The intent action shows network details settings to allow configuration of Wi-Fi.
- * <p>
- * In some cases, a matching Activity may not exist, so ensure you
- * safeguard against this.
- * <p>
- * Input: The calling package should put the chosen
- * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
- * the {@link #KEY_CHOSEN_WIFIENTRY_KEY}.
- * <p>
- * Output: Nothing.
- */
- public static final String ACTION_WIFI_DETAILS_SETTINGS =
- "android.settings.WIFI_DETAILS_SETTINGS";
- public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
- public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
-
- static final int[] WIFI_PIE = getIconsBasedOnFlag();
-
- private static int[] getIconsBasedOnFlag() {
- if (newStatusBarIcons()) {
- return new int[] {
- R.drawable.ic_wifi_0,
- R.drawable.ic_wifi_1,
- R.drawable.ic_wifi_2,
- R.drawable.ic_wifi_3,
- R.drawable.ic_wifi_4
- };
- } else {
- return new int[] {
- com.android.internal.R.drawable.ic_wifi_signal_0,
- com.android.internal.R.drawable.ic_wifi_signal_1,
- com.android.internal.R.drawable.ic_wifi_signal_2,
- com.android.internal.R.drawable.ic_wifi_signal_3,
- com.android.internal.R.drawable.ic_wifi_signal_4
- };
- }
- }
-
- static final int[] NO_INTERNET_WIFI_PIE = getErrorIconsBasedOnFlag();
-
- private static int [] getErrorIconsBasedOnFlag() {
- if (newStatusBarIcons()) {
- return new int[] {
- R.drawable.ic_wifi_0_error,
- R.drawable.ic_wifi_1_error,
- R.drawable.ic_wifi_2_error,
- R.drawable.ic_wifi_3_error,
- R.drawable.ic_wifi_4_error
- };
- } else {
- return new int[] {
- R.drawable.ic_no_internet_wifi_signal_0,
- R.drawable.ic_no_internet_wifi_signal_1,
- R.drawable.ic_no_internet_wifi_signal_2,
- R.drawable.ic_no_internet_wifi_signal_3,
- R.drawable.ic_no_internet_wifi_signal_4
- };
- }
- }
-
- public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) {
- final StringBuilder summary = new StringBuilder();
- final WifiInfo info = accessPoint.getInfo();
- // Add RSSI/band information for this config, what was seen up to 6 seconds ago
- // verbose WiFi Logging is only turned on thru developers settings
- if (accessPoint.isActive() && info != null) {
- summary.append(" f=" + Integer.toString(info.getFrequency()));
- }
- summary.append(" " + getVisibilityStatus(accessPoint));
- if (config != null
- && (config.getNetworkSelectionStatus().getNetworkSelectionStatus()
- != NETWORK_SELECTION_ENABLED)) {
- summary.append(" (" + config.getNetworkSelectionStatus().getNetworkStatusString());
- if (config.getNetworkSelectionStatus().getDisableTime() > 0) {
- long now = System.currentTimeMillis();
- long diff = (now - config.getNetworkSelectionStatus().getDisableTime()) / 1000;
- long sec = diff % 60; //seconds
- long min = (diff / 60) % 60; //minutes
- long hour = (min / 60) % 60; //hours
- summary.append(", ");
- if (hour > 0) summary.append(Long.toString(hour) + "h ");
- summary.append(Long.toString(min) + "m ");
- summary.append(Long.toString(sec) + "s ");
- }
- summary.append(")");
- }
-
- if (config != null) {
- NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
- for (int reason = 0; reason <= getMaxNetworkSelectionDisableReason(); reason++) {
- if (networkStatus.getDisableReasonCounter(reason) != 0) {
- summary.append(" ")
- .append(NetworkSelectionStatus
- .getNetworkSelectionDisableReasonString(reason))
- .append("=")
- .append(networkStatus.getDisableReasonCounter(reason));
- }
- }
- }
-
- return summary.toString();
- }
-
- /**
- * Returns the visibility status of the WifiConfiguration.
- *
- * @return autojoin debugging information
- * TODO: use a string formatter
- * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
- * For instance [-40,5/-30,2]
- */
- @VisibleForTesting
- static String getVisibilityStatus(AccessPoint accessPoint) {
- final WifiInfo info = accessPoint.getInfo();
- StringBuilder visibility = new StringBuilder();
- StringBuilder scans24GHz = new StringBuilder();
- StringBuilder scans5GHz = new StringBuilder();
- StringBuilder scans60GHz = new StringBuilder();
- String bssid = null;
-
- if (accessPoint.isActive() && info != null) {
- bssid = info.getBSSID();
- if (bssid != null) {
- visibility.append(" ").append(bssid);
- }
- visibility.append(" standard = ").append(info.getWifiStandard());
- visibility.append(" rssi=").append(info.getRssi());
- visibility.append(" ");
- visibility.append(" score=").append(info.getScore());
- if (accessPoint.getSpeed() != AccessPoint.Speed.NONE) {
- visibility.append(" speed=").append(accessPoint.getSpeedLabel());
- }
- visibility.append(String.format(" tx=%.1f,", info.getSuccessfulTxPacketsPerSecond()));
- visibility.append(String.format("%.1f,", info.getRetriedTxPacketsPerSecond()));
- visibility.append(String.format("%.1f ", info.getLostTxPacketsPerSecond()));
- visibility.append(String.format("rx=%.1f", info.getSuccessfulRxPacketsPerSecond()));
- }
-
- int maxRssi5 = INVALID_RSSI;
- int maxRssi24 = INVALID_RSSI;
- int maxRssi60 = INVALID_RSSI;
- final int maxDisplayedScans = 4;
- int num5 = 0; // number of scanned BSSID on 5GHz band
- int num24 = 0; // number of scanned BSSID on 2.4Ghz band
- int num60 = 0; // number of scanned BSSID on 60Ghz band
- int numBlockListed = 0;
-
- // TODO: sort list by RSSI or age
- long nowMs = SystemClock.elapsedRealtime();
- for (ScanResult result : accessPoint.getScanResults()) {
- if (result == null) {
- continue;
- }
- if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ
- && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) {
- // Strictly speaking: [4915, 5825]
- num5++;
-
- if (result.level > maxRssi5) {
- maxRssi5 = result.level;
- }
- if (num5 <= maxDisplayedScans) {
- scans5GHz.append(
- verboseScanResultSummary(accessPoint, result, bssid,
- nowMs));
- }
- } else if (result.frequency >= AccessPoint.LOWER_FREQ_24GHZ
- && result.frequency <= AccessPoint.HIGHER_FREQ_24GHZ) {
- // Strictly speaking: [2412, 2482]
- num24++;
-
- if (result.level > maxRssi24) {
- maxRssi24 = result.level;
- }
- if (num24 <= maxDisplayedScans) {
- scans24GHz.append(
- verboseScanResultSummary(accessPoint, result, bssid,
- nowMs));
- }
- } else if (result.frequency >= AccessPoint.LOWER_FREQ_60GHZ
- && result.frequency <= AccessPoint.HIGHER_FREQ_60GHZ) {
- // Strictly speaking: [60000, 61000]
- num60++;
-
- if (result.level > maxRssi60) {
- maxRssi60 = result.level;
- }
- if (num60 <= maxDisplayedScans) {
- scans60GHz.append(
- verboseScanResultSummary(accessPoint, result, bssid,
- nowMs));
- }
- }
- }
- visibility.append(" [");
- if (num24 > 0) {
- visibility.append("(").append(num24).append(")");
- if (num24 > maxDisplayedScans) {
- visibility.append("max=").append(maxRssi24).append(",");
- }
- visibility.append(scans24GHz.toString());
- }
- visibility.append(";");
- if (num5 > 0) {
- visibility.append("(").append(num5).append(")");
- if (num5 > maxDisplayedScans) {
- visibility.append("max=").append(maxRssi5).append(",");
- }
- visibility.append(scans5GHz.toString());
- }
- visibility.append(";");
- if (num60 > 0) {
- visibility.append("(").append(num60).append(")");
- if (num60 > maxDisplayedScans) {
- visibility.append("max=").append(maxRssi60).append(",");
- }
- visibility.append(scans60GHz.toString());
- }
- if (numBlockListed > 0) {
- visibility.append("!").append(numBlockListed);
- }
- visibility.append("]");
-
- return visibility.toString();
- }
-
- @VisibleForTesting
- /* package */ static String verboseScanResultSummary(AccessPoint accessPoint, ScanResult result,
- String bssid, long nowMs) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(" \n{").append(result.BSSID);
- if (result.BSSID.equals(bssid)) {
- stringBuilder.append("*");
- }
- stringBuilder.append("=").append(result.frequency);
- stringBuilder.append(",").append(result.level);
- int speed = getSpecificApSpeed(result, accessPoint.getScoredNetworkCache());
- if (speed != AccessPoint.Speed.NONE) {
- stringBuilder.append(",")
- .append(accessPoint.getSpeedLabel(speed));
- }
- int ageSeconds = (int) (nowMs - result.timestamp / 1000) / 1000;
- stringBuilder.append(",").append(ageSeconds).append("s");
- stringBuilder.append("}");
- return stringBuilder.toString();
- }
-
- @AccessPoint.Speed
- private static int getSpecificApSpeed(ScanResult result,
- Map<String, TimestampedScoredNetwork> scoredNetworkCache) {
- TimestampedScoredNetwork timedScore = scoredNetworkCache.get(result.BSSID);
- if (timedScore == null) {
- return AccessPoint.Speed.NONE;
- }
- // For debugging purposes we may want to use mRssi rather than result.level as the average
- // speed wil be determined by mRssi
- return timedScore.getScore().calculateBadge(result.level);
- }
-
- public static String getMeteredLabel(Context context, WifiConfiguration config) {
- // meteredOverride is whether the user manually set the metered setting or not.
- // meteredHint is whether the network itself is telling us that it is metered
- if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED
- || (config.meteredHint && !isMeteredOverridden(config))) {
- return context.getString(R.string.wifi_metered_label);
- }
- return context.getString(R.string.wifi_unmetered_label);
- }
-
- /**
- * Returns the Internet icon resource for a given RSSI level.
- *
- * @param level The number of bars to show (0-4)
- * @param noInternet True if a connected Wi-Fi network cannot access the Internet
- */
- public static int getInternetIconResource(int level, boolean noInternet) {
- int wifiLevel = level;
- if (wifiLevel < 0) {
- Log.e(TAG, "Wi-Fi level is out of range! level:" + level);
- wifiLevel = 0;
- } else if (level >= WIFI_PIE.length) {
- Log.e(TAG, "Wi-Fi level is out of range! level:" + level);
- wifiLevel = WIFI_PIE.length - 1;
- }
- return noInternet ? NO_INTERNET_WIFI_PIE[wifiLevel] : WIFI_PIE[wifiLevel];
- }
-
- /**
- * Returns the Hotspot network icon resource.
- *
- * @param deviceType The device type of Hotspot network
- */
- public static int getHotspotIconResource(int deviceType) {
- return switch (deviceType) {
- case NetworkProviderInfo.DEVICE_TYPE_PHONE -> R.drawable.ic_hotspot_phone;
- case NetworkProviderInfo.DEVICE_TYPE_TABLET -> R.drawable.ic_hotspot_tablet;
- case NetworkProviderInfo.DEVICE_TYPE_LAPTOP -> R.drawable.ic_hotspot_laptop;
- case NetworkProviderInfo.DEVICE_TYPE_WATCH -> R.drawable.ic_hotspot_watch;
- case NetworkProviderInfo.DEVICE_TYPE_AUTO -> R.drawable.ic_hotspot_auto;
- default -> R.drawable.ic_hotspot_phone; // Return phone icon as default.
- };
- }
-
- /**
- * Wrapper the {@link #getInternetIconResource} for testing compatibility.
- */
- public static class InternetIconInjector {
-
- protected final Context mContext;
-
- public InternetIconInjector(Context context) {
- mContext = context;
- }
-
- /**
- * Returns the Internet icon for a given RSSI level.
- *
- * @param noInternet True if a connected Wi-Fi network cannot access the Internet
- * @param level The number of bars to show (0-4)
- */
- public Drawable getIcon(boolean noInternet, int level) {
- return mContext.getDrawable(WifiUtils.getInternetIconResource(level, noInternet));
- }
- }
-
- public static boolean isMeteredOverridden(WifiConfiguration config) {
- return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
- }
-
- /**
- * Returns the Intent for Wi-Fi dialog.
- *
- * @param key The Wi-Fi entry key
- * @param connectForCaller True if a chosen WifiEntry request to connect to
- */
- public static Intent getWifiDialogIntent(String key, boolean connectForCaller) {
- final Intent intent = new Intent(ACTION_WIFI_DIALOG);
- intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, key);
- intent.putExtra(EXTRA_CONNECT_FOR_CALLER, connectForCaller);
- return intent;
- }
-
- /**
- * Returns the Intent for Wi-Fi network details settings.
- *
- * @param key The Wi-Fi entry key
- */
- public static Intent getWifiDetailsSettingsIntent(String key) {
- final Intent intent = new Intent(ACTION_WIFI_DETAILS_SETTINGS);
- final Bundle bundle = new Bundle();
- bundle.putString(KEY_CHOSEN_WIFIENTRY_KEY, key);
- intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
- return intent;
- }
-
- /**
- * Returns the string of Wi-Fi tethering summary for connected devices.
- *
- * @param context The application context
- * @param connectedDevices The count of connected devices
- */
- public static String getWifiTetherSummaryForConnectedDevices(Context context,
- int connectedDevices) {
- MessageFormat msgFormat = new MessageFormat(
- context.getResources().getString(R.string.wifi_tether_connected_summary),
- Locale.getDefault());
- Map<String, Object> arguments = new HashMap<>();
- arguments.put("count", connectedDevices);
- return msgFormat.format(arguments);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
new file mode 100644
index 000000000000..d5444cf44b02
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.wifi
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import android.icu.text.MessageFormat
+import android.net.wifi.ScanResult
+import android.net.wifi.WifiConfiguration
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus
+import android.net.wifi.WifiManager
+import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
+import android.os.Bundle
+import android.os.SystemClock
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import com.android.settingslib.R
+import com.android.settingslib.flags.Flags.newStatusBarIcons
+import java.util.Locale
+import kotlin.coroutines.resume
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+
+
+open class WifiUtils {
+ /**
+ * Wrapper the [.getInternetIconResource] for testing compatibility.
+ */
+ open class InternetIconInjector(protected val context: Context) {
+ /**
+ * Returns the Internet icon for a given RSSI level.
+ *
+ * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+ * @param level The number of bars to show (0-4)
+ */
+ open fun getIcon(noInternet: Boolean, level: Int): Drawable? {
+ return context.getDrawable(getInternetIconResource(level, noInternet))
+ }
+ }
+
+ companion object {
+ private const val TAG = "WifiUtils"
+ private const val INVALID_RSSI = -127
+
+ /**
+ * The intent action shows Wi-Fi dialog to connect Wi-Fi network.
+ *
+ *
+ * Input: The calling package should put the chosen
+ * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
+ * the [.EXTRA_CHOSEN_WIFI_ENTRY_KEY].
+ *
+ *
+ * Output: Nothing.
+ */
+ @JvmField
+ @VisibleForTesting
+ val ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG"
+
+ /**
+ * Specify a key that indicates the WifiEntry to be configured.
+ */
+ @JvmField
+ @VisibleForTesting
+ val EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key"
+
+ /**
+ * The lookup key for a boolean that indicates whether a chosen WifiEntry request to connect to.
+ * `true` means a chosen WifiEntry request to connect to.
+ */
+ @JvmField
+ @VisibleForTesting
+ val EXTRA_CONNECT_FOR_CALLER = "connect_for_caller"
+
+ /**
+ * The intent action shows network details settings to allow configuration of Wi-Fi.
+ *
+ *
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ *
+ *
+ * Input: The calling package should put the chosen
+ * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
+ * the [.KEY_CHOSEN_WIFIENTRY_KEY].
+ *
+ *
+ * Output: Nothing.
+ */
+ const val ACTION_WIFI_DETAILS_SETTINGS = "android.settings.WIFI_DETAILS_SETTINGS"
+ const val KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key"
+ const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
+
+ @JvmField
+ val WIFI_PIE = getIconsBasedOnFlag()
+
+ private fun getIconsBasedOnFlag(): IntArray {
+ return if (newStatusBarIcons()) {
+ intArrayOf(
+ R.drawable.ic_wifi_0,
+ R.drawable.ic_wifi_1,
+ R.drawable.ic_wifi_2,
+ R.drawable.ic_wifi_3,
+ R.drawable.ic_wifi_4
+ )
+ } else {
+ intArrayOf(
+ com.android.internal.R.drawable.ic_wifi_signal_0,
+ com.android.internal.R.drawable.ic_wifi_signal_1,
+ com.android.internal.R.drawable.ic_wifi_signal_2,
+ com.android.internal.R.drawable.ic_wifi_signal_3,
+ com.android.internal.R.drawable.ic_wifi_signal_4
+ )
+ }
+ }
+
+ val NO_INTERNET_WIFI_PIE = getErrorIconsBasedOnFlag()
+
+ private fun getErrorIconsBasedOnFlag(): IntArray {
+ return if (newStatusBarIcons()) {
+ intArrayOf(
+ R.drawable.ic_wifi_0_error,
+ R.drawable.ic_wifi_1_error,
+ R.drawable.ic_wifi_2_error,
+ R.drawable.ic_wifi_3_error,
+ R.drawable.ic_wifi_4_error
+ )
+ } else {
+ intArrayOf(
+ R.drawable.ic_no_internet_wifi_signal_0,
+ R.drawable.ic_no_internet_wifi_signal_1,
+ R.drawable.ic_no_internet_wifi_signal_2,
+ R.drawable.ic_no_internet_wifi_signal_3,
+ R.drawable.ic_no_internet_wifi_signal_4
+ )
+ }
+ }
+
+ @JvmStatic
+ fun buildLoggingSummary(accessPoint: AccessPoint, config: WifiConfiguration?): String {
+ val summary = StringBuilder()
+ val info = accessPoint.info
+ // Add RSSI/band information for this config, what was seen up to 6 seconds ago
+ // verbose WiFi Logging is only turned on thru developers settings
+ if (accessPoint.isActive && info != null) {
+ summary.append(" f=" + info.frequency.toString())
+ }
+ summary.append(" " + getVisibilityStatus(accessPoint))
+ if (config != null && (config.networkSelectionStatus.networkSelectionStatus
+ != NetworkSelectionStatus.NETWORK_SELECTION_ENABLED)
+ ) {
+ summary.append(" (" + config.networkSelectionStatus.networkStatusString)
+ if (config.networkSelectionStatus.disableTime > 0) {
+ val now = System.currentTimeMillis()
+ val diff = (now - config.networkSelectionStatus.disableTime) / 1000
+ val sec = diff % 60 // seconds
+ val min = diff / 60 % 60 // minutes
+ val hour = min / 60 % 60 // hours
+ summary.append(", ")
+ if (hour > 0) summary.append(hour.toString() + "h ")
+ summary.append(min.toString() + "m ")
+ summary.append(sec.toString() + "s ")
+ }
+ summary.append(")")
+ }
+ if (config != null) {
+ val networkStatus = config.networkSelectionStatus
+ for (reason in 0..NetworkSelectionStatus.getMaxNetworkSelectionDisableReason()) {
+ if (networkStatus.getDisableReasonCounter(reason) != 0) {
+ summary.append(" ")
+ .append(
+ NetworkSelectionStatus
+ .getNetworkSelectionDisableReasonString(reason)
+ )
+ .append("=")
+ .append(networkStatus.getDisableReasonCounter(reason))
+ }
+ }
+ }
+ return summary.toString()
+ }
+
+ /**
+ * Returns the visibility status of the WifiConfiguration.
+ *
+ * @return autojoin debugging information
+ * TODO: use a string formatter
+ * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
+ * For instance [-40,5/-30,2]
+ */
+ @JvmStatic
+ @VisibleForTesting
+ fun getVisibilityStatus(accessPoint: AccessPoint): String {
+ val info = accessPoint.info
+ val visibility = StringBuilder()
+ val scans24GHz = StringBuilder()
+ val scans5GHz = StringBuilder()
+ val scans60GHz = StringBuilder()
+ var bssid: String? = null
+ if (accessPoint.isActive && info != null) {
+ bssid = info.bssid
+ if (bssid != null) {
+ visibility.append(" ").append(bssid)
+ }
+ visibility.append(" standard = ").append(info.wifiStandard)
+ visibility.append(" rssi=").append(info.rssi)
+ visibility.append(" ")
+ visibility.append(" score=").append(info.getScore())
+ if (accessPoint.speed != AccessPoint.Speed.NONE) {
+ visibility.append(" speed=").append(accessPoint.speedLabel)
+ }
+ visibility.append(String.format(" tx=%.1f,", info.successfulTxPacketsPerSecond))
+ visibility.append(String.format("%.1f,", info.retriedTxPacketsPerSecond))
+ visibility.append(String.format("%.1f ", info.lostTxPacketsPerSecond))
+ visibility.append(String.format("rx=%.1f", info.successfulRxPacketsPerSecond))
+ }
+ var maxRssi5 = INVALID_RSSI
+ var maxRssi24 = INVALID_RSSI
+ var maxRssi60 = INVALID_RSSI
+ val maxDisplayedScans = 4
+ var num5 = 0 // number of scanned BSSID on 5GHz band
+ var num24 = 0 // number of scanned BSSID on 2.4Ghz band
+ var num60 = 0 // number of scanned BSSID on 60Ghz band
+ val numBlockListed = 0
+
+ // TODO: sort list by RSSI or age
+ val nowMs = SystemClock.elapsedRealtime()
+ for (result in accessPoint.getScanResults()) {
+ if (result == null) {
+ continue
+ }
+ if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ &&
+ result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ
+ ) {
+ // Strictly speaking: [4915, 5825]
+ num5++
+ if (result.level > maxRssi5) {
+ maxRssi5 = result.level
+ }
+ if (num5 <= maxDisplayedScans) {
+ scans5GHz.append(
+ verboseScanResultSummary(
+ accessPoint, result, bssid,
+ nowMs
+ )
+ )
+ }
+ } else if (result.frequency >= AccessPoint.LOWER_FREQ_24GHZ &&
+ result.frequency <= AccessPoint.HIGHER_FREQ_24GHZ
+ ) {
+ // Strictly speaking: [2412, 2482]
+ num24++
+ if (result.level > maxRssi24) {
+ maxRssi24 = result.level
+ }
+ if (num24 <= maxDisplayedScans) {
+ scans24GHz.append(
+ verboseScanResultSummary(
+ accessPoint, result, bssid,
+ nowMs
+ )
+ )
+ }
+ } else if (result.frequency >= AccessPoint.LOWER_FREQ_60GHZ &&
+ result.frequency <= AccessPoint.HIGHER_FREQ_60GHZ
+ ) {
+ // Strictly speaking: [60000, 61000]
+ num60++
+ if (result.level > maxRssi60) {
+ maxRssi60 = result.level
+ }
+ if (num60 <= maxDisplayedScans) {
+ scans60GHz.append(
+ verboseScanResultSummary(
+ accessPoint, result, bssid,
+ nowMs
+ )
+ )
+ }
+ }
+ }
+ visibility.append(" [")
+ if (num24 > 0) {
+ visibility.append("(").append(num24).append(")")
+ if (num24 > maxDisplayedScans) {
+ visibility.append("max=").append(maxRssi24).append(",")
+ }
+ visibility.append(scans24GHz.toString())
+ }
+ visibility.append(";")
+ if (num5 > 0) {
+ visibility.append("(").append(num5).append(")")
+ if (num5 > maxDisplayedScans) {
+ visibility.append("max=").append(maxRssi5).append(",")
+ }
+ visibility.append(scans5GHz.toString())
+ }
+ visibility.append(";")
+ if (num60 > 0) {
+ visibility.append("(").append(num60).append(")")
+ if (num60 > maxDisplayedScans) {
+ visibility.append("max=").append(maxRssi60).append(",")
+ }
+ visibility.append(scans60GHz.toString())
+ }
+ if (numBlockListed > 0) {
+ visibility.append("!").append(numBlockListed)
+ }
+ visibility.append("]")
+ return visibility.toString()
+ }
+
+ @JvmStatic
+ @VisibleForTesting /* package */ fun verboseScanResultSummary(
+ accessPoint: AccessPoint,
+ result: ScanResult,
+ bssid: String?,
+ nowMs: Long
+ ): String {
+ val stringBuilder = StringBuilder()
+ stringBuilder.append(" \n{").append(result.BSSID)
+ if (result.BSSID == bssid) {
+ stringBuilder.append("*")
+ }
+ stringBuilder.append("=").append(result.frequency)
+ stringBuilder.append(",").append(result.level)
+ val speed = getSpecificApSpeed(result, accessPoint.scoredNetworkCache)
+ if (speed != AccessPoint.Speed.NONE) {
+ stringBuilder.append(",")
+ .append(accessPoint.getSpeedLabel(speed))
+ }
+ val ageSeconds = (nowMs - result.timestamp / 1000).toInt() / 1000
+ stringBuilder.append(",").append(ageSeconds).append("s")
+ stringBuilder.append("}")
+ return stringBuilder.toString()
+ }
+
+ @AccessPoint.Speed
+ private fun getSpecificApSpeed(
+ result: ScanResult,
+ scoredNetworkCache: Map<String, TimestampedScoredNetwork>
+ ): Int {
+ val timedScore = scoredNetworkCache[result.BSSID] ?: return AccessPoint.Speed.NONE
+ // For debugging purposes we may want to use mRssi rather than result.level as the average
+ // speed wil be determined by mRssi
+ return timedScore.score.calculateBadge(result.level)
+ }
+
+ @JvmStatic
+ fun getMeteredLabel(context: Context, config: WifiConfiguration): String {
+ // meteredOverride is whether the user manually set the metered setting or not.
+ // meteredHint is whether the network itself is telling us that it is metered
+ return if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED ||
+ config.meteredHint && !isMeteredOverridden(
+ config
+ )
+ ) {
+ context.getString(R.string.wifi_metered_label)
+ } else context.getString(R.string.wifi_unmetered_label)
+ }
+
+ /**
+ * Returns the Internet icon resource for a given RSSI level.
+ *
+ * @param level The number of bars to show (0-4)
+ * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+ */
+ @JvmStatic
+ fun getInternetIconResource(level: Int, noInternet: Boolean): Int {
+ var wifiLevel = level
+ if (wifiLevel < 0) {
+ Log.e(TAG, "Wi-Fi level is out of range! level:$level")
+ wifiLevel = 0
+ } else if (level >= WIFI_PIE.size) {
+ Log.e(TAG, "Wi-Fi level is out of range! level:$level")
+ wifiLevel = WIFI_PIE.size - 1
+ }
+ return if (noInternet) NO_INTERNET_WIFI_PIE[wifiLevel] else WIFI_PIE[wifiLevel]
+ }
+
+ /**
+ * Returns the Hotspot network icon resource.
+ *
+ * @param deviceType The device type of Hotspot network
+ */
+ @JvmStatic
+ fun getHotspotIconResource(deviceType: Int): Int {
+ return when (deviceType) {
+ NetworkProviderInfo.DEVICE_TYPE_PHONE -> R.drawable.ic_hotspot_phone
+ NetworkProviderInfo.DEVICE_TYPE_TABLET -> R.drawable.ic_hotspot_tablet
+ NetworkProviderInfo.DEVICE_TYPE_LAPTOP -> R.drawable.ic_hotspot_laptop
+ NetworkProviderInfo.DEVICE_TYPE_WATCH -> R.drawable.ic_hotspot_watch
+ NetworkProviderInfo.DEVICE_TYPE_AUTO -> R.drawable.ic_hotspot_auto
+ else -> R.drawable.ic_hotspot_phone
+ }
+ }
+
+ @JvmStatic
+ fun isMeteredOverridden(config: WifiConfiguration): Boolean {
+ return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE
+ }
+
+ /**
+ * Returns the Intent for Wi-Fi dialog.
+ *
+ * @param key The Wi-Fi entry key
+ * @param connectForCaller True if a chosen WifiEntry request to connect to
+ */
+ @JvmStatic
+ fun getWifiDialogIntent(key: String?, connectForCaller: Boolean): Intent {
+ val intent = Intent(ACTION_WIFI_DIALOG)
+ intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, key)
+ intent.putExtra(EXTRA_CONNECT_FOR_CALLER, connectForCaller)
+ return intent
+ }
+
+ /**
+ * Returns the Intent for Wi-Fi network details settings.
+ *
+ * @param key The Wi-Fi entry key
+ */
+ @JvmStatic
+ fun getWifiDetailsSettingsIntent(key: String?): Intent {
+ val intent = Intent(ACTION_WIFI_DETAILS_SETTINGS)
+ val bundle = Bundle()
+ bundle.putString(KEY_CHOSEN_WIFIENTRY_KEY, key)
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle)
+ return intent
+ }
+
+ /**
+ * Returns the string of Wi-Fi tethering summary for connected devices.
+ *
+ * @param context The application context
+ * @param connectedDevices The count of connected devices
+ */
+ @JvmStatic
+ fun getWifiTetherSummaryForConnectedDevices(
+ context: Context,
+ connectedDevices: Int
+ ): String {
+ val msgFormat = MessageFormat(
+ context.resources.getString(R.string.wifi_tether_connected_summary),
+ Locale.getDefault()
+ )
+ val arguments: MutableMap<String, Any> = HashMap()
+ arguments["count"] = connectedDevices
+ return msgFormat.format(arguments)
+ }
+
+ @JvmStatic
+ fun checkWepAllowed(
+ context: Context,
+ lifecycleOwner: LifecycleOwner,
+ ssid: String,
+ onAllowed: () -> Unit,
+ ) {
+ lifecycleOwner.lifecycleScope.launch {
+ val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
+ if (wifiManager.queryWepAllowed()) {
+ onAllowed()
+ } else {
+ val intent = Intent(Intent.ACTION_MAIN).apply {
+ component = ComponentName(
+ "com.android.settings",
+ "com.android.settings.network.WepNetworkDialogActivity"
+ )
+ putExtra(SSID, ssid)
+ }
+ context.startActivity(intent)
+ }
+ }
+ }
+
+ private suspend fun WifiManager.queryWepAllowed(): Boolean =
+ withContext(Dispatchers.Default) {
+ suspendCancellableCoroutine { continuation ->
+ queryWepAllowed(Dispatchers.Default.asExecutor()) {
+ continuation.resume(it)
+ }
+ }
+ }
+
+ const val SSID = "ssid"
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
index 2a5c43cae289..9ab17c49bbec 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
@@ -37,9 +37,10 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.UserHandle;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
+import androidx.test.filters.SmallTest;
+
import com.android.settingslib.BaseTest;
import org.mockito.ArgumentMatcher;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index 2522e956a25e..ab28d061419c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -41,9 +41,10 @@ import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.inputmethod.InputMethodInfo;
+import androidx.test.filters.SmallTest;
+
import com.android.settingslib.BaseTest;
import org.mockito.ArgumentMatcher;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 827d8fa9e7c1..3b18aa310c91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -72,6 +72,7 @@ import com.android.settingslib.testutils.shadow.ShadowUserManager;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -499,6 +500,7 @@ public class ApplicationsStateRoboTest {
verify(mApplicationsState, never()).clearEntries();
}
+ @Ignore("b/328332487")
@Test
public void removeProfileApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries()
throws RemoteException {
@@ -573,6 +575,7 @@ public class ApplicationsStateRoboTest {
verify(mApplicationsState).clearEntries();
}
+ @Ignore("b/328332487")
@Test
public void removeOwnerApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries()
throws RemoteException {
@@ -654,6 +657,7 @@ public class ApplicationsStateRoboTest {
verify(mApplicationsState).clearEntries();
}
+ @Ignore("b/328332487")
@Test
public void noAppRemoved_workprofileExists_doResumeIfNeededLocked_shouldNotClearEntries()
throws RemoteException {
@@ -773,6 +777,7 @@ public class ApplicationsStateRoboTest {
assertThat(primaryUserApp.shouldShowInPersonalTab(um, appInfo.uid)).isTrue();
}
+ @Ignore("b/328332487")
@Test
public void shouldShowInPersonalTab_userProfilePreU_returnsFalse() {
UserManager um = RuntimeEnvironment.application.getSystemService(UserManager.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 461ecf5d3c84..5996dbb322fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -18,9 +18,7 @@ package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -58,8 +56,6 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-import java.util.concurrent.ExecutionException;
-
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class CachedBluetoothDeviceTest {
@@ -1823,52 +1819,6 @@ public class CachedBluetoothDeviceTest {
assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
}
- @Test
- public void syncProfileForMemberDevice_hasDiff_shouldSync()
- throws ExecutionException, InterruptedException {
- mCachedDevice.addMemberDevice(mSubCachedDevice);
- when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
- when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
- when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
-
- when(mA2dpProfile.isEnabled(mDevice)).thenReturn(true);
- when(mHearingAidProfile.isEnabled(mDevice)).thenReturn(true);
- when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
-
- when(mA2dpProfile.isEnabled(mSubDevice)).thenReturn(true);
- when(mHearingAidProfile.isEnabled(mSubDevice)).thenReturn(false);
- when(mLeAudioProfile.isEnabled(mSubDevice)).thenReturn(false);
-
- mCachedDevice.syncProfileForMemberDevice().get();
-
- verify(mA2dpProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- verify(mHearingAidProfile).setEnabled(any(BluetoothDevice.class), eq(true));
- verify(mLeAudioProfile).setEnabled(any(BluetoothDevice.class), eq(true));
- }
-
- @Test
- public void syncProfileForMemberDevice_noDiff_shouldNotSync()
- throws ExecutionException, InterruptedException {
- mCachedDevice.addMemberDevice(mSubCachedDevice);
- when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
- when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
- when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
-
- when(mA2dpProfile.isEnabled(mDevice)).thenReturn(false);
- when(mHearingAidProfile.isEnabled(mDevice)).thenReturn(false);
- when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
-
- when(mA2dpProfile.isEnabled(mSubDevice)).thenReturn(false);
- when(mHearingAidProfile.isEnabled(mSubDevice)).thenReturn(false);
- when(mLeAudioProfile.isEnabled(mSubDevice)).thenReturn(true);
-
- mCachedDevice.syncProfileForMemberDevice().get();
-
- verify(mA2dpProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- verify(mHearingAidProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- verify(mLeAudioProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- }
-
private HearingAidInfo getLeftAshaHearingAidInfo() {
return new HearingAidInfo.Builder()
.setAshaDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
index 46e724d245f5..c3237f0e72eb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
@@ -16,7 +16,6 @@
package com.android.settingslib.media;
-import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
@@ -32,6 +31,8 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.Collections;
+
@RunWith(RobolectricTestRunner.class)
public class MediaManagerTest {
@@ -59,7 +60,7 @@ public class MediaManagerTest {
public void dispatchDeviceListAdded_registerCallback_shouldDispatchCallback() {
mMediaManager.registerCallback(mCallback);
- mMediaManager.dispatchDeviceListAdded();
+ mMediaManager.dispatchDeviceListAdded(Collections.emptyList());
verify(mCallback).onDeviceListAdded(any());
}
@@ -68,9 +69,9 @@ public class MediaManagerTest {
public void dispatchDeviceListRemoved_registerCallback_shouldDispatchCallback() {
mMediaManager.registerCallback(mCallback);
- mMediaManager.dispatchDeviceListRemoved(mMediaManager.mMediaDevices);
+ mMediaManager.dispatchDeviceListRemoved(Collections.emptyList());
- verify(mCallback).onDeviceListRemoved(mMediaManager.mMediaDevices);
+ verify(mCallback).onDeviceListRemoved(Collections.emptyList());
}
@Test
@@ -83,24 +84,6 @@ public class MediaManagerTest {
}
@Test
- public void findMediaDevice_idExist_shouldReturnMediaDevice() {
- mMediaManager.mMediaDevices.add(mDevice);
-
- final MediaDevice device = mMediaManager.findMediaDevice(TEST_ID);
-
- assertThat(device.getId()).isEqualTo(mDevice.getId());
- }
-
- @Test
- public void findMediaDevice_idNotExist_shouldReturnNull() {
- mMediaManager.mMediaDevices.add(mDevice);
-
- final MediaDevice device = mMediaManager.findMediaDevice("123");
-
- assertThat(device).isNull();
- }
-
- @Test
public void dispatchOnRequestFailed_registerCallback_shouldDispatchCallback() {
mMediaManager.registerCallback(mCallback);
diff --git a/packages/SettingsProvider/res/xml/bookmarks.xml b/packages/SettingsProvider/res/xml/bookmarks.xml
index 4b97b4779e1b..22d02262c388 100644
--- a/packages/SettingsProvider/res/xml/bookmarks.xml
+++ b/packages/SettingsProvider/res/xml/bookmarks.xml
@@ -23,7 +23,7 @@
'c': Contacts
'e': Email
'g': GMail
- 'l': Calendar
+ 'k': Calendar
'm': Maps
'p': Music
's': SMS
@@ -33,7 +33,7 @@
-->
<bookmarks>
<bookmark
- category="android.intent.category.APP_BROWSER"
+ role="android.app.role.BROWSER"
shortcut="b" />
<bookmark
category="android.intent.category.APP_CONTACTS"
@@ -51,7 +51,7 @@
category="android.intent.category.APP_MUSIC"
shortcut="p" />
<bookmark
- category="android.intent.category.APP_MESSAGING"
+ role="android.app.role.SMS"
shortcut="s" />
<bookmark
category="android.intent.category.APP_CALCULATOR"
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index add313419c7d..3043d54b3025 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -116,5 +116,8 @@ public class GlobalSettings {
Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
Settings.Global.FORCE_ENABLE_PSS_PROFILING,
+ Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
+ Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
+ Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_SPEED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2fa1c6eda458..a490b6f096ef 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -154,6 +154,7 @@ public class SecureSettings {
Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
Settings.Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED,
+ Settings.Secure.EMERGENCY_THERMAL_ALERT_DISABLED,
Settings.Secure.HUSH_GESTURE_USED,
Settings.Secure.IN_CALL_NOTIFICATION_ENABLED,
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 2535fdb6e4d0..4cdf98cbe14e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -235,6 +235,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.MANUAL_RINGER_TOGGLE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.EMERGENCY_THERMAL_ALERT_DISABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.IN_CALL_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 6ff36d43f91f..ad3eb92b0519 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -20,8 +20,6 @@ import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
-import static com.android.providers.settings.Flags.supportOverrides;
-
import android.aconfig.Aconfig.parsed_flag;
import android.aconfig.Aconfig.parsed_flags;
import android.annotation.SuppressLint;
@@ -269,9 +267,9 @@ public final class DeviceConfigService extends Binder {
verb = CommandVerb.GET;
} else if ("put".equalsIgnoreCase(cmd)) {
verb = CommandVerb.PUT;
- } else if (supportOverrides() && "override".equalsIgnoreCase(cmd)) {
+ } else if ("override".equalsIgnoreCase(cmd)) {
verb = CommandVerb.OVERRIDE;
- } else if (supportOverrides() && "clear_override".equalsIgnoreCase(cmd)) {
+ } else if ("clear_override".equalsIgnoreCase(cmd)) {
verb = CommandVerb.CLEAR_OVERRIDE;
} else if ("delete".equalsIgnoreCase(cmd)) {
verb = CommandVerb.DELETE;
@@ -285,7 +283,7 @@ public final class DeviceConfigService extends Binder {
if (peekNextArg() == null) {
isValid = true;
}
- } else if (supportOverrides() && "list_local_overrides".equalsIgnoreCase(cmd)) {
+ } else if ("list_local_overrides".equalsIgnoreCase(cmd)) {
verb = CommandVerb.LIST_LOCAL_OVERRIDES;
if (peekNextArg() == null) {
isValid = true;
@@ -427,14 +425,10 @@ public final class DeviceConfigService extends Binder {
DeviceConfig.setProperty(namespace, key, value, makeDefault);
break;
case OVERRIDE:
- if (supportOverrides()) {
- DeviceConfig.setLocalOverride(namespace, key, value);
- }
+ DeviceConfig.setLocalOverride(namespace, key, value);
break;
case CLEAR_OVERRIDE:
- if (supportOverrides()) {
- DeviceConfig.clearLocalOverride(namespace, key);
- }
+ DeviceConfig.clearLocalOverride(namespace, key);
break;
case DELETE:
pout.println(delete(iprovider, namespace, key)
@@ -452,19 +446,15 @@ public final class DeviceConfigService extends Binder {
}
} else {
for (String line : listAll(iprovider)) {
- if (supportOverrides()) {
- boolean isPrivate = false;
- for (String privateNamespace : PRIVATE_NAMESPACES) {
- if (line.startsWith(privateNamespace)) {
- isPrivate = true;
- break;
- }
+ boolean isPrivate = false;
+ for (String privateNamespace : PRIVATE_NAMESPACES) {
+ if (line.startsWith(privateNamespace)) {
+ isPrivate = true;
+ break;
}
+ }
- if (!isPrivate) {
- pout.println(line);
- }
- } else {
+ if (!isPrivate) {
pout.println(line);
}
}
@@ -495,18 +485,16 @@ public final class DeviceConfigService extends Binder {
}
break;
case LIST_LOCAL_OVERRIDES:
- if (supportOverrides()) {
- Map<String, Map<String, String>> underlyingValues =
- DeviceConfig.getUnderlyingValuesForOverriddenFlags();
- for (String overrideNamespace : underlyingValues.keySet()) {
- Map<String, String> flagToValue =
- underlyingValues.get(overrideNamespace);
- for (String flag : flagToValue.keySet()) {
- String flagText = overrideNamespace + "/" + flag;
- String valueText =
- DeviceConfig.getProperty(overrideNamespace, flag);
- pout.println(flagText + "=" + valueText);
- }
+ Map<String, Map<String, String>> underlyingValues =
+ DeviceConfig.getUnderlyingValuesForOverriddenFlags();
+ for (String overrideNamespace : underlyingValues.keySet()) {
+ Map<String, String> flagToValue =
+ underlyingValues.get(overrideNamespace);
+ for (String flag : flagToValue.keySet()) {
+ String flagText = overrideNamespace + "/" + flag;
+ String valueText =
+ DeviceConfig.getProperty(overrideNamespace, flag);
+ pout.println(flagText + "=" + valueText);
}
}
break;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index febce97031bb..1ead14ab6f4c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3812,7 +3812,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 225;
+ private static final int SETTINGS_VERSION = 226;
private final int mUserId;
@@ -6011,6 +6011,28 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 225;
}
+ // Version 225: Set the System#KEYBOARD_VIBRATION_ENABLED based on touch
+ // feedback enabled state.
+ if (currentVersion == 225) {
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ final Setting touchFeedbackSettings = systemSettings
+ .getSettingLocked(Settings.System.HAPTIC_FEEDBACK_ENABLED);
+ final Setting keyboardVibrationSettings = systemSettings
+ .getSettingLocked(Settings.System.KEYBOARD_VIBRATION_ENABLED);
+ if (keyboardVibrationSettings.isNull()) {
+ if (!touchFeedbackSettings.isNull()) {
+ // Use touch feedback settings.
+ systemSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.System.KEYBOARD_VIBRATION_ENABLED,
+ touchFeedbackSettings.getValue(),
+ touchFeedbackSettings.getTag(),
+ touchFeedbackSettings.isDefaultFromSystem(),
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+ currentVersion = 226;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index ce0257f6c85b..2a8eb9bc0845 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -72,6 +72,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -157,6 +158,9 @@ final class SettingsState {
"/product/etc/aconfig_flags.pb",
"/vendor/etc/aconfig_flags.pb");
+ private static final String APEX_DIR = "/apex";
+ private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb";
+
/**
* This tag is applied to all aconfig default value-loaded flags.
*/
@@ -238,7 +242,7 @@ final class SettingsState {
private int mNextHistoricalOpIdx;
@GuardedBy("mLock")
- @Nullable
+ @NonNull
private Map<String, Map<String, String>> mNamespaceDefaults;
public static final int SETTINGS_TYPE_GLOBAL = 0;
@@ -332,23 +336,29 @@ final class SettingsState {
mHistoricalOperations = Build.IS_DEBUGGABLE
? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
+ mNamespaceDefaults = new HashMap<>();
+
synchronized (mLock) {
readStateSyncLocked();
if (Flags.loadAconfigDefaults()) {
if (isConfigSettingsKey(mKey)) {
- loadAconfigDefaultValuesLocked();
+ loadAconfigDefaultValuesLocked(sAconfigTextProtoFilesOnDevice);
}
}
+ if (Flags.loadApexAconfigProtobufs()) {
+ if (isConfigSettingsKey(mKey)) {
+ List<String> apexProtoPaths = listApexProtoPaths();
+ loadAconfigDefaultValuesLocked(apexProtoPaths);
+ }
+ }
}
}
@GuardedBy("mLock")
- private void loadAconfigDefaultValuesLocked() {
- mNamespaceDefaults = new HashMap<>();
-
- for (String fileName : sAconfigTextProtoFilesOnDevice) {
+ private void loadAconfigDefaultValuesLocked(List<String> filePaths) {
+ for (String fileName : filePaths) {
try (FileInputStream inputStream = new FileInputStream(fileName)) {
loadAconfigDefaultValues(inputStream.readAllBytes(), mNamespaceDefaults);
} catch (IOException e) {
@@ -357,13 +367,41 @@ final class SettingsState {
}
}
+ private List<String> listApexProtoPaths() {
+ LinkedList<String> paths = new LinkedList();
+
+ File apexDirectory = new File(APEX_DIR);
+ if (!apexDirectory.isDirectory()) {
+ return paths;
+ }
+
+ File[] subdirs = apexDirectory.listFiles();
+ if (subdirs == null) {
+ return paths;
+ }
+
+ for (File prefix : subdirs) {
+ // For each mainline modules, there are two directories, one <modulepackage>/,
+ // and one <modulepackage>@<versioncode>/. Just read the former.
+ if (prefix.getAbsolutePath().contains("@")) {
+ continue;
+ }
+
+ File protoPath = new File(prefix + APEX_ACONFIG_PATH_SUFFIX);
+ if (!protoPath.exists()) {
+ continue;
+ }
+
+ paths.add(protoPath.getAbsolutePath());
+ }
+ return paths;
+ }
+
@VisibleForTesting
@GuardedBy("mLock")
public void addAconfigDefaultValuesFromMap(
@NonNull Map<String, Map<String, String>> defaultMap) {
- if (mNamespaceDefaults != null) {
- mNamespaceDefaults.putAll(defaultMap);
- }
+ mNamespaceDefaults.putAll(defaultMap);
}
@VisibleForTesting
@@ -447,7 +485,7 @@ final class SettingsState {
return names;
}
- @Nullable
+ @NonNull
public Map<String, Map<String, String>> getAconfigDefaultValues() {
synchronized (mLock) {
return mNamespaceDefaults;
@@ -519,9 +557,9 @@ final class SettingsState {
return false;
}
- // Aconfig flags are always boot stable, so we anytime we write one, we staged it to be
+ // Aconfig flags are always boot stable, so we anytime we write one, we stage it to be
// applied on reboot.
- if (Flags.stageAllAconfigFlags() && mNamespaceDefaults != null) {
+ if (Flags.stageAllAconfigFlags()) {
int slashIndex = name.indexOf("/");
boolean stageFlag = isConfigSettingsKey(mKey)
&& slashIndex != -1
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index e5086e87173a..2e14e9b8be4c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -25,3 +25,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "load_apex_aconfig_protobufs"
+ namespace: "core_experiments_team_internal"
+ description: "When enabled, loads aconfig default values in apex flag protobufs into DeviceConfig on boot."
+ bug: "327383546"
+ is_fixed_read_only: true
+}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 28cdc6db192b..09d076ee9c26 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -626,9 +626,6 @@ public class SettingsBackupTest {
Settings.Global.Wearable.BEDTIME_MODE,
Settings.Global.Wearable.BEDTIME_HARD_MODE,
Settings.Global.Wearable.LOCK_SCREEN_STATE,
- Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
- Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
- Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_SPEED,
Settings.Global.Wearable.DISABLE_AOD_WHILE_PLUGGED,
Settings.Global.Wearable.NETWORK_LOCATION_OPT_IN,
Settings.Global.Wearable.CUSTOM_COLOR_FOREGROUND,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index ea30c69b1c45..33362a22ffba 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -25,7 +25,6 @@ import android.aconfig.Aconfig;
import android.aconfig.Aconfig.parsed_flag;
import android.aconfig.Aconfig.parsed_flags;
import android.os.Looper;
-import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -231,38 +230,6 @@ public class SettingsStateTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_LOAD_ACONFIG_DEFAULTS)
- public void testAddingAconfigMapOnNullIsNoOp() {
- int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
- Object lock = new Object();
- SettingsState settingsState = new SettingsState(
- InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
- SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
-
- parsed_flags flags = parsed_flags
- .newBuilder()
- .addParsedFlag(parsed_flag
- .newBuilder()
- .setPackage("com.android.flags")
- .setName("flag5")
- .setNamespace("test_namespace")
- .setDescription("test flag")
- .addBug("12345678")
- .setState(Aconfig.flag_state.DISABLED)
- .setPermission(Aconfig.flag_permission.READ_WRITE))
- .build();
-
- synchronized (lock) {
- Map<String, Map<String, String>> defaults = new HashMap<>();
- settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
- settingsState.addAconfigDefaultValuesFromMap(defaults);
-
- assertEquals(null, settingsState.getAconfigDefaultValues());
- }
-
- }
-
- @Test
public void testInvalidAconfigProtoDoesNotCrash() {
Map<String, Map<String, String>> defaults = new HashMap<>();
SettingsState settingsState = getSettingStateObject();
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 53f2caf0b793..0c02f56ab294 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -210,6 +210,7 @@
<uses-permission android:name="android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
+ <uses-permission android:name="android.permission.EMERGENCY_INSTALL_PACKAGES" />
<!-- Permission required for processes that don't own the focused window to switch
touch mode state -->
<uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" />
diff --git a/packages/Shell/tests/src/com/android/shell/UtilitiesTest.java b/packages/Shell/tests/src/com/android/shell/UtilitiesTest.java
index 51b7ba8529a8..ad9eec7f3303 100644
--- a/packages/Shell/tests/src/com/android/shell/UtilitiesTest.java
+++ b/packages/Shell/tests/src/com/android/shell/UtilitiesTest.java
@@ -15,10 +15,12 @@
*/
package com.android.shell;
-import android.test.suitebuilder.annotation.SmallTest;
-import junit.framework.TestCase;
import static com.android.shell.BugreportProgressService.isValid;
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
@SmallTest
public class UtilitiesTest extends TestCase {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e63232a87499..04cb88d5a39d 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -32,55 +32,6 @@ license {
],
}
-// Opt-in configuration for code depending on Jetpack Compose.
-soong_config_module_type {
- name: "systemui_compose_java_defaults",
- module_type: "java_defaults",
- config_namespace: "ANDROID",
- bool_variables: ["SYSTEMUI_USE_COMPOSE"],
- properties: [
- "srcs",
- "static_libs",
- ],
-}
-
-systemui_compose_java_defaults {
- name: "SystemUI_compose_defaults",
- soong_config_variables: {
- SYSTEMUI_USE_COMPOSE: {
- // Because files in compose/features/ depend on SystemUI
- // code, we compile those files when compiling SystemUI-core.
- // We also compile the ComposeFacade in
- // compose/facade/enabled/.
- srcs: [
- "compose/features/src/**/*.kt",
- "compose/facade/enabled/src/**/*.kt",
- ],
-
- // The dependencies needed by SystemUIComposeFeatures,
- // except for SystemUI-core.
- // Copied from compose/features/Android.bp.
- static_libs: [
- "PlatformComposeCore",
- "PlatformComposeSceneTransitionLayout",
-
- "androidx.compose.runtime_runtime",
- "androidx.compose.material3_material3",
- "androidx.compose.material_material-icons-extended",
- "androidx.activity_activity-compose",
- "androidx.compose.animation_animation-graphics",
- ],
-
- // By default, Compose is disabled and we compile the ComposeFacade
- // in compose/facade/disabled/.
- conditions_default: {
- srcs: ["compose/facade/disabled/src/**/*.kt"],
- static_libs: [],
- },
- },
- },
-}
-
java_library {
name: "SystemUI-proto",
@@ -138,14 +89,13 @@ android_library {
android_library {
name: "SystemUI-core",
- defaults: [
- "SystemUI_compose_defaults",
- ],
srcs: [
"src/**/*.kt",
"src/**/*.java",
"src/**/I*.aidl",
":ReleaseJavaFiles",
+ "compose/features/src/**/*.kt",
+ "compose/facade/enabled/src/**/*.kt",
],
product_variables: {
debuggable: {
@@ -207,6 +157,13 @@ android_library {
"LowLightDreamLib",
"motion_tool_lib",
"notification_flags_lib",
+ "PlatformComposeCore",
+ "PlatformComposeSceneTransitionLayout",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.activity_activity-compose",
+ "androidx.compose.animation_animation-graphics",
],
libs: [
"keepanno-annotations",
@@ -337,16 +294,21 @@ android_library {
"ravenwood-junit",
"platform-test-annotations",
"notification_flags_lib",
+ "PlatformComposeCore",
+ "PlatformComposeSceneTransitionLayout",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.activity_activity-compose",
+ "androidx.compose.animation_animation-graphics",
],
}
android_library {
name: "SystemUI-tests",
use_resource_processor: true,
- defaults: [
- "SystemUI_compose_defaults",
- ],
manifest: "tests/AndroidManifest-base.xml",
+ resource_dirs: [],
additional_manifests: ["tests/AndroidManifest.xml"],
srcs: [
"tests/src/**/*.kt",
@@ -357,6 +319,8 @@ android_library {
":ReleaseJavaFiles",
":SystemUI-tests-multivalent",
":SystemUI-tests-utils",
+ "compose/features/src/**/*.kt",
+ "compose/facade/enabled/src/**/*.kt",
],
static_libs: [
"SystemUI-tests-base",
@@ -367,6 +331,7 @@ android_library {
"androidx.test.ext.truth",
"kotlin-test",
"SystemUICustomizationTestUtils",
+ "androidx.compose.runtime_runtime",
],
libs: [
"android.test.runner",
@@ -396,7 +361,6 @@ android_app {
defaults: [
"platform_app_defaults",
"SystemUI_optimized_defaults",
- "SystemUI_compose_defaults",
],
manifest: "tests/AndroidManifest-base.xml",
@@ -405,9 +369,12 @@ android_app {
"src/**/*.java",
"src/**/I*.aidl",
":ReleaseJavaFiles",
+ "compose/features/src/**/*.kt",
+ "compose/facade/enabled/src/**/*.kt",
],
static_libs: [
"SystemUI-tests-base",
+ "androidx.compose.runtime_runtime",
],
libs: [
"keepanno-annotations",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 085fc2959442..88181e7e2a52 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -56,10 +56,12 @@ public class AccessibilityMenuService extends AccessibilityService
implements View.OnTouchListener {
public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName();
+ public static final String PACKAGE_TESTS = ".tests";
public static final String INTENT_TOGGLE_MENU = ".toggle_menu";
public static final String INTENT_HIDE_MENU = ".hide_menu";
public static final String INTENT_GLOBAL_ACTION = ".global_action";
public static final String INTENT_GLOBAL_ACTION_EXTRA = "GLOBAL_ACTION";
+ public static final String INTENT_OPEN_BLOCKED = "OPEN_BLOCKED";
private static final String TAG = "A11yMenuService";
private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
@@ -192,7 +194,7 @@ public class AccessibilityMenuService extends AccessibilityService
IntentFilter hideMenuFilter = new IntentFilter();
hideMenuFilter.addAction(Intent.ACTION_SCREEN_OFF);
- hideMenuFilter.addAction(PACKAGE_NAME + INTENT_HIDE_MENU);
+ hideMenuFilter.addAction(INTENT_HIDE_MENU);
// Including WRITE_SECURE_SETTINGS enforces that we only listen to apps
// with the restricted WRITE_SECURE_SETTINGS permission who broadcast this intent.
@@ -200,7 +202,7 @@ public class AccessibilityMenuService extends AccessibilityService
Manifest.permission.WRITE_SECURE_SETTINGS, null,
Context.RECEIVER_EXPORTED);
registerReceiver(mToggleMenuReceiver,
- new IntentFilter(PACKAGE_NAME + INTENT_TOGGLE_MENU),
+ new IntentFilter(INTENT_TOGGLE_MENU),
Manifest.permission.WRITE_SECURE_SETTINGS, null,
Context.RECEIVER_EXPORTED);
@@ -245,8 +247,9 @@ public class AccessibilityMenuService extends AccessibilityService
* @return {@code true} if successful, {@code false} otherwise.
*/
private boolean performGlobalActionInternal(int globalAction) {
- Intent intent = new Intent(PACKAGE_NAME + INTENT_GLOBAL_ACTION);
+ Intent intent = new Intent(INTENT_GLOBAL_ACTION);
intent.putExtra(INTENT_GLOBAL_ACTION_EXTRA, globalAction);
+ intent.setPackage(PACKAGE_NAME + PACKAGE_TESTS);
sendBroadcast(intent);
Log.i("A11yMenuService", "Broadcasting global action " + globalAction);
return performGlobalAction(globalAction);
@@ -410,9 +413,16 @@ public class AccessibilityMenuService extends AccessibilityService
private void toggleVisibility() {
boolean locked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
- if (!locked && SystemClock.uptimeMillis() - mLastTimeTouchedOutside
- > BUTTON_CLICK_TIMEOUT) {
- mA11yMenuLayout.toggleVisibility();
+ if (!locked) {
+ if (SystemClock.uptimeMillis() - mLastTimeTouchedOutside
+ > BUTTON_CLICK_TIMEOUT) {
+ mA11yMenuLayout.toggleVisibility();
+ }
+ } else {
+ // Broadcast for testing.
+ Intent intent = new Intent(INTENT_OPEN_BLOCKED);
+ intent.setPackage(PACKAGE_NAME + PACKAGE_TESTS);
+ sendBroadcast(intent);
}
}
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 72c1092b92d6..6546b87c8802 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -23,6 +23,7 @@ import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_QU
import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_RECENTS;
import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_OPEN_BLOCKED;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION_EXTRA;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;
@@ -65,6 +66,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@RunWith(AndroidJUnit4.class)
@@ -75,12 +77,11 @@ public class AccessibilityMenuServiceTest {
private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
private static final int TIMEOUT_UI_CHANGE_S = 5;
private static final int NO_GLOBAL_ACTION = -1;
- private static final String INPUT_KEYEVENT_KEYCODE_BACK = "input keyevent KEYCODE_BACK";
- private static final String TEST_PIN = "1234";
private static Instrumentation sInstrumentation;
private static UiAutomation sUiAutomation;
- private static AtomicInteger sLastGlobalAction;
+ private static final AtomicInteger sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION);
+ private static final AtomicBoolean sOpenBlocked = new AtomicBoolean(false);
private static AccessibilityManager sAccessibilityManager;
private static PowerManager sPowerManager;
@@ -122,8 +123,6 @@ public class AccessibilityMenuServiceTest {
() -> sAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
info -> info.getId().contains(serviceName)).count() == 1);
-
- sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION);
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -131,20 +130,28 @@ public class AccessibilityMenuServiceTest {
sLastGlobalAction.set(
intent.getIntExtra(INTENT_GLOBAL_ACTION_EXTRA, NO_GLOBAL_ACTION));
}},
- new IntentFilter(PACKAGE_NAME + INTENT_GLOBAL_ACTION),
+ new IntentFilter(INTENT_GLOBAL_ACTION),
+ null, null, Context.RECEIVER_EXPORTED);
+
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received notification that menu cannot be opened.");
+ sOpenBlocked.set(true);
+ }},
+ new IntentFilter(INTENT_OPEN_BLOCKED),
null, null, Context.RECEIVER_EXPORTED);
}
@AfterClass
- public static void classTeardown() throws Throwable {
- clearPin();
+ public static void classTeardown() {
Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
}
@Before
public void setup() throws Throwable {
- clearPin();
+ sOpenBlocked.set(false);
wakeUpScreen();
sUiAutomation.executeShellCommand("input keyevent KEYCODE_MENU");
openMenu();
@@ -154,20 +161,8 @@ public class AccessibilityMenuServiceTest {
public void tearDown() throws Throwable {
closeMenu();
sLastGlobalAction.set(NO_GLOBAL_ACTION);
- }
-
- private static void clearPin() throws Throwable {
- sUiAutomation.executeShellCommand("locksettings clear --old " + TEST_PIN);
- TestUtils.waitUntil("Device did not register as unlocked & insecure.",
- TIMEOUT_SERVICE_STATUS_CHANGE_S,
- () -> !sKeyguardManager.isDeviceSecure());
- }
-
- private static void setPin() throws Throwable {
- sUiAutomation.executeShellCommand("locksettings set-pin " + TEST_PIN);
- TestUtils.waitUntil("Device did not recognize as locked & secure.",
- TIMEOUT_SERVICE_STATUS_CHANGE_S,
- () -> sKeyguardManager.isDeviceSecure());
+ // dismisses screenshot popup if present.
+ sUiAutomation.executeShellCommand("input keyevent KEYCODE_BACK");
}
private static boolean isMenuVisible() {
@@ -184,7 +179,6 @@ public class AccessibilityMenuServiceTest {
private static void closeScreen() throws Throwable {
Display display = sDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- setPin();
sUiAutomation.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN);
TestUtils.waitUntil("Screen did not close.",
TIMEOUT_UI_CHANGE_S,
@@ -194,12 +188,20 @@ public class AccessibilityMenuServiceTest {
}
private static void openMenu() throws Throwable {
- Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU);
+ openMenu(false);
+ }
+
+ private static void openMenu(boolean abandonOnBlock) throws Throwable {
+ Intent intent = new Intent(INTENT_TOGGLE_MENU);
+ intent.setPackage(PACKAGE_NAME);
sInstrumentation.getContext().sendBroadcast(intent);
TestUtils.waitUntil("Timed out before menu could appear.",
TIMEOUT_UI_CHANGE_S,
() -> {
+ if (sOpenBlocked.get() && abandonOnBlock) {
+ throw new IllegalStateException();
+ }
if (isMenuVisible()) {
return true;
} else {
@@ -213,7 +215,8 @@ public class AccessibilityMenuServiceTest {
if (!isMenuVisible()) {
return;
}
- Intent intent = new Intent(PACKAGE_NAME + INTENT_HIDE_MENU);
+ Intent intent = new Intent(INTENT_HIDE_MENU);
+ intent.setPackage(PACKAGE_NAME);
sInstrumentation.getContext().sendBroadcast(intent);
TestUtils.waitUntil("Timed out before menu could close.",
TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());
@@ -444,13 +447,13 @@ public class AccessibilityMenuServiceTest {
closeScreen();
wakeUpScreen();
- boolean timedOut = false;
+ boolean blocked = false;
try {
- openMenu();
- } catch (AssertionError e) {
+ openMenu(true);
+ } catch (IllegalStateException e) {
// Expected
- timedOut = true;
+ blocked = true;
}
- assertThat(timedOut).isTrue();
+ assertThat(blocked).isTrue();
}
}
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
index d0e6b2865891..7bbe82c212e7 100644
--- a/packages/SystemUI/aconfig/predictive_back.aconfig
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -4,26 +4,26 @@ flag {
name: "predictive_back_sysui"
namespace: "systemui"
description: "Predictive Back Dispatching for SysUI"
- bug: "309545085"
+ bug: "327737297"
}
flag {
name: "predictive_back_animate_shade"
namespace: "systemui"
description: "Enable Shade Animations"
- bug: "309545085"
+ bug: "327732946"
}
flag {
name: "predictive_back_animate_bouncer"
namespace: "systemui"
description: "Enable Predictive Back Animation in Bouncer"
- bug: "309545085"
+ bug: "327733487"
}
flag {
name: "predictive_back_animate_dialogs"
namespace: "systemui"
description: "Enable Predictive Back Animation for SysUI dialogs"
- bug: "309545085"
+ bug: "327721544"
} \ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ba7738005de2..d05d40d969de 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -25,6 +25,16 @@ flag {
}
flag {
+ name: "notification_view_flipper_pausing"
+ namespace: "systemui"
+ description: "Pause ViewFlippers inside Notification custom layouts when the shade is closed."
+ bug: "309146176"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_async_group_header_inflation"
namespace: "systemui"
description: "Inflates the notification group summary header views from the background thread."
@@ -76,6 +86,16 @@ flag {
}
flag {
+ name: "notification_content_alpha_optimization"
+ namespace: "systemui"
+ description: "Only reset alpha values of needed content views"
+ bug: "292024656"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notifications_live_data_store_refactor"
namespace: "systemui"
description: "Replaces NotifLiveDataStore with ActiveNotificationListRepository, and updates consumers. "
@@ -84,6 +104,17 @@ flag {
}
flag {
+ name: "pss_app_selector_abrupt_exit_fix"
+ namespace: "systemui"
+ description: "Fixes the app selector abruptly disappearing without an animation, when the"
+ "selected task is the foreground task."
+ bug: "314385883"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notifications_background_media_icons"
namespace: "systemui"
description: "Updates icons for media notifications in the background."
@@ -173,14 +204,6 @@ flag {
}
flag {
- name: "keyguard_shade_migration_nssl"
- namespace: "systemui"
- description: "Moves NSSL into a shared element between the notification_panel and "
- "keyguard_root_view."
- bug: "278054201"
-}
-
-flag {
name: "unfold_animation_background_progress"
namespace: "systemui"
description: "Moves unfold animation progress calculation to a background thread"
@@ -398,6 +421,13 @@ flag {
}
flag {
+ name: "smartspace_remoteviews_rendering"
+ namespace: "systemui"
+ description: "Indicate Smartspace RemoteViews rendering"
+ bug: "326292691"
+}
+
+flag {
name: "pin_input_field_styled_focus_state"
namespace: "systemui"
description: "Enables styled focus states on pin input field if keyboard is connected"
@@ -447,6 +477,13 @@ flag {
}
flag {
+ name: "hearing_aids_qs_tile_dialog"
+ namespace: "systemui"
+ description: "Show a dialog when clicking on hearing aids quick settings tile."
+ bug: "291423171"
+}
+
+flag {
name: "notification_row_user_context"
namespace: "systemui"
description: "Create a user-specific Context for the ImageResolver in ExpandableNotificationRow"
@@ -542,6 +579,9 @@ flag {
namespace: "systemui"
description: "Binds Keyguard Media Controller Visibility to MediaContainerView"
bug: "298213983"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -563,3 +603,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "media_controls_refactor"
+ namespace: "systemui"
+ description: "Refactors media code to follow the recommended architecture"
+ bug: "326408371"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 8dc74951d332..b1240252796f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -27,6 +27,7 @@ import android.graphics.fonts.FontVariationAxis
import android.text.Layout
import android.util.LruCache
import kotlin.math.roundToInt
+import android.util.Log
private const val DEFAULT_ANIMATION_DURATION: Long = 300
private const val TYPEFACE_CACHE_MAX_ENTRIES = 5
@@ -140,7 +141,6 @@ class TextAnimator(
}
sealed class PositionedGlyph {
-
/** Mutable X coordinate of the glyph position relative from drawing offset. */
var x: Float = 0f
@@ -269,41 +269,53 @@ class TextAnimator(
duration: Long = -1L,
interpolator: TimeInterpolator? = null,
delay: Long = 0,
- onAnimationEnd: Runnable? = null
+ onAnimationEnd: Runnable? = null,
+ ) = setTextStyleInternal(fvar, textSize, color, strokeWidth, animate, duration,
+ interpolator, delay, onAnimationEnd, updateLayoutOnFailure = true)
+
+ private fun setTextStyleInternal(
+ fvar: String?,
+ textSize: Float,
+ color: Int?,
+ strokeWidth: Float,
+ animate: Boolean,
+ duration: Long,
+ interpolator: TimeInterpolator?,
+ delay: Long,
+ onAnimationEnd: Runnable?,
+ updateLayoutOnFailure: Boolean,
) {
- if (animate) {
- animator.cancel()
- textInterpolator.rebase()
- }
-
- if (textSize >= 0) {
- textInterpolator.targetPaint.textSize = textSize
- }
-
- if (!fvar.isNullOrBlank()) {
- textInterpolator.targetPaint.typeface = typefaceCache.getTypefaceForVariant(fvar)
- }
+ try {
+ if (animate) {
+ animator.cancel()
+ textInterpolator.rebase()
+ }
- if (color != null) {
- textInterpolator.targetPaint.color = color
- }
- if (strokeWidth >= 0F) {
- textInterpolator.targetPaint.strokeWidth = strokeWidth
- }
- textInterpolator.onTargetPaintModified()
-
- if (animate) {
- animator.startDelay = delay
- animator.duration =
- if (duration == -1L) {
- DEFAULT_ANIMATION_DURATION
- } else {
- duration
- }
- interpolator?.let { animator.interpolator = it }
- if (onAnimationEnd != null) {
- val listener =
- object : AnimatorListenerAdapter() {
+ if (textSize >= 0) {
+ textInterpolator.targetPaint.textSize = textSize
+ }
+ if (!fvar.isNullOrBlank()) {
+ textInterpolator.targetPaint.typeface = typefaceCache.getTypefaceForVariant(fvar)
+ }
+ if (color != null) {
+ textInterpolator.targetPaint.color = color
+ }
+ if (strokeWidth >= 0F) {
+ textInterpolator.targetPaint.strokeWidth = strokeWidth
+ }
+ textInterpolator.onTargetPaintModified()
+
+ if (animate) {
+ animator.startDelay = delay
+ animator.duration =
+ if (duration == -1L) {
+ DEFAULT_ANIMATION_DURATION
+ } else {
+ duration
+ }
+ interpolator?.let { animator.interpolator = it }
+ if (onAnimationEnd != null) {
+ val listener = object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
onAnimationEnd.run()
animator.removeListener(this)
@@ -312,14 +324,25 @@ class TextAnimator(
animator.removeListener(this)
}
}
- animator.addListener(listener)
+ animator.addListener(listener)
+ }
+ animator.start()
+ } else {
+ // No animation is requested, thus set base and target state to the same state.
+ textInterpolator.progress = 1f
+ textInterpolator.rebase()
+ invalidateCallback()
+ }
+ } catch (ex: IllegalArgumentException) {
+ if (updateLayoutOnFailure) {
+ Log.e(TAG, "setTextStyleInternal: Exception caught but retrying. This is usually" +
+ " due to the layout having changed unexpectedly without being notified.", ex)
+ updateLayout(textInterpolator.layout)
+ setTextStyleInternal(fvar, textSize, color, strokeWidth, animate, duration,
+ interpolator, delay, onAnimationEnd, updateLayoutOnFailure = false)
+ } else {
+ throw ex
}
- animator.start()
- } else {
- // No animation is requested, thus set base and target state to the same state.
- textInterpolator.progress = 1f
- textInterpolator.rebase()
- invalidateCallback()
}
}
@@ -355,15 +378,13 @@ class TextAnimator(
interpolator: TimeInterpolator? = null,
delay: Long = 0,
onAnimationEnd: Runnable? = null
- ) {
- val fvar = fontVariationUtils.updateFontVariation(
- weight = weight,
- width = width,
- opticalSize = opticalSize,
- roundness = roundness,
- )
- setTextStyle(
- fvar = fvar,
+ ) = setTextStyleInternal(
+ fvar = fontVariationUtils.updateFontVariation(
+ weight = weight,
+ width = width,
+ opticalSize = opticalSize,
+ roundness = roundness,
+ ),
textSize = textSize,
color = color,
strokeWidth = strokeWidth,
@@ -372,6 +393,10 @@ class TextAnimator(
interpolator = interpolator,
delay = delay,
onAnimationEnd = onAnimationEnd,
+ updateLayoutOnFailure = true,
)
+
+ companion object {
+ private val TAG = TextAnimator::class.simpleName!!
}
}
diff --git a/packages/SystemUI/compose/core/TEST_MAPPING b/packages/SystemUI/compose/core/TEST_MAPPING
index ee95f737c113..b71c5fb29fd7 100644
--- a/packages/SystemUI/compose/core/TEST_MAPPING
+++ b/packages/SystemUI/compose/core/TEST_MAPPING
@@ -12,17 +12,6 @@
]
},
{
- "name": "SystemUIComposeFeaturesTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "SystemUIComposeGalleryTests",
"options": [
{
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
deleted file mode 100644
index 4398b2541f65..000000000000
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.compose
-
-import android.content.Context
-import android.view.View
-import android.view.WindowInsets
-import androidx.activity.ComponentActivity
-import androidx.lifecycle.LifecycleOwner
-import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.scene.shared.model.Scene
-import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.StateFlow
-
-/** The Compose facade, when Compose is *not* available. */
-object ComposeFacade : BaseComposeFacade {
- override fun isComposeAvailable(): Boolean = false
-
- override fun composeInitializer(): ComposeInitializer {
- throwComposeUnavailableError()
- }
-
- override fun setPeopleSpaceActivityContent(
- activity: ComponentActivity,
- viewModel: PeopleViewModel,
- onResult: (PeopleViewModel.Result) -> Unit,
- ) {
- throwComposeUnavailableError()
- }
-
- override fun setCommunalEditWidgetActivityContent(
- activity: ComponentActivity,
- viewModel: BaseCommunalViewModel,
- widgetConfigurator: WidgetConfigurator,
- onOpenWidgetPicker: () -> Unit,
- onEditDone: () -> Unit,
- ) {
- throwComposeUnavailableError()
- }
-
- override fun setVolumePanelActivityContent(
- activity: ComponentActivity,
- viewModel: VolumePanelViewModel,
- onDismiss: () -> Unit,
- ) {
- throwComposeUnavailableError()
- }
-
- override fun createFooterActionsView(
- context: Context,
- viewModel: FooterActionsViewModel,
- qsVisibilityLifecycleOwner: LifecycleOwner
- ): View {
- throwComposeUnavailableError()
- }
-
- override fun createSceneContainerView(
- scope: CoroutineScope,
- context: Context,
- viewModel: SceneContainerViewModel,
- windowInsets: StateFlow<WindowInsets?>,
- sceneByKey: Map<SceneKey, Scene>,
- dataSourceDelegator: SceneDataSourceDelegator,
- ): View {
- throwComposeUnavailableError()
- }
-
- override fun createStickyKeysIndicatorContent(
- context: Context,
- viewModel: StickyKeysIndicatorViewModel
- ): View {
- throwComposeUnavailableError()
- }
-
- override fun createCommunalView(
- context: Context,
- viewModel: BaseCommunalViewModel,
- ): View {
- throwComposeUnavailableError()
- }
-
- override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
- throwComposeUnavailableError()
- }
-
- override fun createBouncer(
- context: Context,
- viewModel: BouncerViewModel,
- dialogFactory: BouncerDialogFactory,
- ): View = throwComposeUnavailableError()
-
- override fun createLockscreen(
- context: Context,
- viewModel: LockscreenContentViewModel,
- blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
- ): View = throwComposeUnavailableError()
-
- private fun throwComposeUnavailableError(): Nothing {
- error(
- "Compose is not available. Make sure to check isComposeAvailable() before calling any" +
- " other function on ComposeFacade."
- )
- }
-}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/BouncerSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/BouncerSceneModule.kt
deleted file mode 100644
index 1a5e22b2c5ee..000000000000
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/BouncerSceneModule.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene
-
-import dagger.Module
-
-@Module interface BouncerSceneModule
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
deleted file mode 100644
index 387b05644f14..000000000000
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene
-
-import dagger.Module
-
-@Module interface QuickSettingsSceneModule
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/ShadeSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/ShadeSceneModule.kt
deleted file mode 100644
index 232c4211c7c1..000000000000
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/ShadeSceneModule.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene
-
-import dagger.Module
-
-@Module interface ShadeSceneModule
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
deleted file mode 100644
index a4fb05d3b5b9..000000000000
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.anc
-
-import dagger.Module
-
-@Module interface AncModule
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt
deleted file mode 100644
index c8dae76f9f1a..000000000000
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.bottombar
-
-import dagger.Module
-
-@Module interface BottomBarModule
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt
deleted file mode 100644
index b4cb0983f213..000000000000
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.volume
-
-import dagger.Module
-
-@Module interface VolumeSlidersModule
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
deleted file mode 100644
index 76931a2b4d41..000000000000
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.compose
-
-import android.content.Context
-import android.graphics.Point
-import android.view.View
-import android.view.WindowInsets
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.LifecycleOwner
-import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.compose.theme.PlatformTheme
-import com.android.compose.ui.platform.DensityAwareComposeView
-import com.android.internal.policy.ScreenDecorationsUtils
-import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.composable.BouncerContent
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
-import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
-import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
-import com.android.systemui.communal.ui.compose.CommunalContainer
-import com.android.systemui.communal.ui.compose.CommunalHub
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView
-import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
-import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.people.ui.compose.PeopleScreen
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import com.android.systemui.qs.footer.ui.compose.FooterActions
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.scene.shared.model.Scene
-import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.ui.composable.ComposableScene
-import com.android.systemui.scene.ui.composable.SceneContainer
-import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
-import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-
-/** The Compose facade, when Compose is available. */
-object ComposeFacade : BaseComposeFacade {
- override fun isComposeAvailable(): Boolean = true
-
- override fun composeInitializer(): ComposeInitializer = ComposeInitializerImpl
-
- override fun setPeopleSpaceActivityContent(
- activity: ComponentActivity,
- viewModel: PeopleViewModel,
- onResult: (PeopleViewModel.Result) -> Unit,
- ) {
- activity.setContent { PlatformTheme { PeopleScreen(viewModel, onResult) } }
- }
-
- override fun setCommunalEditWidgetActivityContent(
- activity: ComponentActivity,
- viewModel: BaseCommunalViewModel,
- widgetConfigurator: WidgetConfigurator,
- onOpenWidgetPicker: () -> Unit,
- onEditDone: () -> Unit,
- ) {
- activity.setContent {
- PlatformTheme {
- Box(
- modifier =
- Modifier.fillMaxSize()
- .background(LocalAndroidColorScheme.current.outlineVariant),
- ) {
- CommunalHub(
- viewModel = viewModel,
- onOpenWidgetPicker = onOpenWidgetPicker,
- widgetConfigurator = widgetConfigurator,
- onEditDone = onEditDone,
- )
- }
- }
- }
- }
-
- override fun setVolumePanelActivityContent(
- activity: ComponentActivity,
- viewModel: VolumePanelViewModel,
- onDismiss: () -> Unit,
- ) {
- activity.setContent {
- VolumePanelRoot(
- viewModel = viewModel,
- onDismiss = onDismiss,
- )
- }
- }
-
- override fun createFooterActionsView(
- context: Context,
- viewModel: FooterActionsViewModel,
- qsVisibilityLifecycleOwner: LifecycleOwner,
- ): View {
- return DensityAwareComposeView(context).apply {
- setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
- }
- }
-
- override fun createSceneContainerView(
- scope: CoroutineScope,
- context: Context,
- viewModel: SceneContainerViewModel,
- windowInsets: StateFlow<WindowInsets?>,
- sceneByKey: Map<SceneKey, Scene>,
- dataSourceDelegator: SceneDataSourceDelegator,
- ): View {
- return ComposeView(context).apply {
- setContent {
- PlatformTheme {
- ScreenDecorProvider(
- displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
- screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
- ) {
- SceneContainer(
- viewModel = viewModel,
- sceneByKey =
- sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
- dataSourceDelegator = dataSourceDelegator,
- )
- }
- }
- }
- }
- }
-
- override fun createStickyKeysIndicatorContent(
- context: Context,
- viewModel: StickyKeysIndicatorViewModel
- ): View {
- return createStickyKeyIndicatorView(context, viewModel)
- }
-
- override fun createCommunalView(
- context: Context,
- viewModel: BaseCommunalViewModel,
- ): View {
- return ComposeView(context).apply {
- setContent { PlatformTheme { CommunalHub(viewModel = viewModel) } }
- }
- }
-
- override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
- return ComposeView(context).apply {
- setContent { PlatformTheme { CommunalContainer(viewModel = viewModel) } }
- }
- }
-
- // TODO(b/298525212): remove once Compose exposes window inset bounds.
- private fun displayCutoutFromWindowInsets(
- scope: CoroutineScope,
- context: Context,
- windowInsets: StateFlow<WindowInsets?>,
- ): StateFlow<DisplayCutout> =
- windowInsets
- .map {
- val boundingRect = it?.displayCutout?.boundingRectTop
- val width = boundingRect?.let { boundingRect.right - boundingRect.left } ?: 0
- val left = boundingRect?.left?.toDp(context) ?: 0.dp
- val top = boundingRect?.top?.toDp(context) ?: 0.dp
- val right = boundingRect?.right?.toDp(context) ?: 0.dp
- val bottom = boundingRect?.bottom?.toDp(context) ?: 0.dp
- val location =
- when {
- width <= 0f -> CutoutLocation.NONE
- left <= 0.dp -> CutoutLocation.LEFT
- right >= getDisplayWidth(context) -> CutoutLocation.RIGHT
- else -> CutoutLocation.CENTER
- }
- DisplayCutout(
- left,
- top,
- right,
- bottom,
- location,
- )
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout())
-
- // TODO(b/298525212): remove once Compose exposes window inset bounds.
- private fun getDisplayWidth(context: Context): Dp {
- val point = Point()
- checkNotNull(context.display).getRealSize(point)
- return point.x.dp
- }
-
- // TODO(b/298525212): remove once Compose exposes window inset bounds.
- private fun Int.toDp(context: Context): Dp {
- return (this.toFloat() / context.resources.displayMetrics.density).dp
- }
-
- override fun createBouncer(
- context: Context,
- viewModel: BouncerViewModel,
- dialogFactory: BouncerDialogFactory,
- ): View {
- return ComposeView(context).apply {
- setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
- }
- }
-
- override fun createLockscreen(
- context: Context,
- viewModel: LockscreenContentViewModel,
- blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
- ): View {
- val sceneBlueprints =
- blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet()
- return ComposeView(context).apply {
- setContent {
- LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints)
- .Content(modifier = Modifier.fillMaxSize())
- }
- }
- }
-}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt
deleted file mode 100644
index 1674591c30b5..000000000000
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.compose
-
-import android.view.View
-import androidx.lifecycle.findViewTreeLifecycleOwner
-import androidx.lifecycle.setViewTreeLifecycleOwner
-import androidx.lifecycle.Lifecycle
-import androidx.savedstate.SavedStateRegistryController
-import androidx.savedstate.SavedStateRegistryOwner
-import com.android.compose.animation.ViewTreeSavedStateRegistryOwner
-import com.android.systemui.lifecycle.ViewLifecycleOwner
-
-internal object ComposeInitializerImpl : ComposeInitializer {
- override fun onAttachedToWindow(root: View) {
- if (root.findViewTreeLifecycleOwner() != null) {
- error("root $root already has a LifecycleOwner")
- }
-
- val parent = root.parent
- if (parent is View && parent.id != android.R.id.content) {
- error(
- "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." +
- "Outside of activities and dialogs, this is usually the top-most View of a " +
- "window."
- )
- }
-
- // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is
- // both visible and focused.
- val lifecycleOwner = ViewLifecycleOwner(root)
-
- // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save
- // or restore because SystemUI process is always running and top-level windows using this
- // initializer are created once, when the process is started.
- val savedStateRegistryOwner =
- object : SavedStateRegistryOwner {
- private val savedStateRegistryController =
- SavedStateRegistryController.create(this).apply { performRestore(null) }
-
- override val savedStateRegistry = savedStateRegistryController.savedStateRegistry
-
- override val lifecycle: Lifecycle
- get() = lifecycleOwner.lifecycle
- }
-
- // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner]
- // because `onCreate` might move the lifecycle state to STARTED which will make
- // [SavedStateRegistryController.performRestore] throw.
- lifecycleOwner.onCreate()
-
- // Set the owners on the root. They will be reused by any ComposeView inside the root
- // hierarchy.
- root.setViewTreeLifecycleOwner(lifecycleOwner)
- ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner)
- }
-
- override fun onDetachedFromWindow(root: View) {
- (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy()
- root.setViewTreeLifecycleOwner(null)
- ViewTreeSavedStateRegistryOwner.set(root, null)
- }
-}
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
deleted file mode 100644
index dfb3a55b0966..000000000000
--- a/packages/SystemUI/compose/features/Android.bp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2022 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 {
- default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
-}
-
-android_library {
- name: "SystemUIComposeFeatures",
- use_resource_processor: true,
- manifest: "AndroidManifest.xml",
-
- srcs: [
- "src/**/*.kt",
- ],
-
- static_libs: [
- "SystemUI-core",
- "PlatformComposeCore",
- "PlatformComposeSceneTransitionLayout",
-
- "androidx.compose.runtime_runtime",
- "androidx.compose.animation_animation-graphics",
- "androidx.compose.material3_material3",
- "androidx.compose.material_material-icons-extended",
- "androidx.activity_activity-compose",
- ],
-
- kotlincflags: ["-Xjvm-default=all"],
- skip_jarjar_repackage: true,
-}
diff --git a/packages/SystemUI/compose/features/AndroidManifest.xml b/packages/SystemUI/compose/features/AndroidManifest.xml
deleted file mode 100644
index c1a9ec55c227..000000000000
--- a/packages/SystemUI/compose/features/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2022 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.compose.features">
-
-</manifest>
diff --git a/packages/SystemUI/compose/features/TEST_MAPPING b/packages/SystemUI/compose/features/TEST_MAPPING
deleted file mode 100644
index 7430acb2e900..000000000000
--- a/packages/SystemUI/compose/features/TEST_MAPPING
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "presubmit": [
- {
- "name": "SystemUIComposeFeaturesTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
- "name": "SystemUIComposeGalleryTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 0469cbe519ea..3ec5508c81b3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -22,15 +22,17 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -52,13 +54,13 @@ constructor(
private val viewModel: BouncerViewModel,
private val dialogFactory: BouncerDialogFactory,
) : ComposableScene {
- override val key = SceneKey.Bouncer
+ override val key = Scenes.Bouncer
override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
MutableStateFlow(
mapOf(
- UserAction.Back to UserActionResult(SceneKey.Lockscreen),
- UserAction.Swipe(Direction.DOWN) to UserActionResult(SceneKey.Lockscreen),
+ Back to UserActionResult(Scenes.Lockscreen),
+ Swipe(SwipeDirection.Down) to UserActionResult(Scenes.Lockscreen),
)
)
.asStateFlow()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index bd539a740e81..2a13d4931b69 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -51,6 +51,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformIconButton
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
+import com.android.systemui.common.ui.compose.SelectedUserAwareInputConnection
import com.android.systemui.res.R
/** UI for the input part of a password-requiring version of the bouncer. */
@@ -71,6 +72,7 @@ internal fun PasswordBouncer(
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
val isImeSwitcherButtonVisible by viewModel.isImeSwitcherButtonVisible.collectAsState()
+ val selectedUserId by viewModel.selectedUserId.collectAsState()
DisposableEffect(Unit) {
viewModel.onShown()
@@ -87,47 +89,51 @@ internal fun PasswordBouncer(
val color = MaterialTheme.colorScheme.onSurfaceVariant
val lineWidthPx = with(LocalDensity.current) { 2.dp.toPx() }
- TextField(
- value = password,
- onValueChange = viewModel::onPasswordInputChanged,
- enabled = isInputEnabled,
- visualTransformation = PasswordVisualTransformation(),
- singleLine = true,
- textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
- keyboardOptions =
- KeyboardOptions(
- keyboardType = KeyboardType.Password,
- imeAction = ImeAction.Done,
- ),
- keyboardActions =
- KeyboardActions(
- onDone = { viewModel.onAuthenticateKeyPressed() },
- ),
- modifier =
- modifier
- .focusRequester(focusRequester)
- .onFocusChanged { viewModel.onTextFieldFocusChanged(it.isFocused) }
- .drawBehind {
- drawLine(
- color = color,
- start = Offset(x = 0f, y = size.height - lineWidthPx),
- end = Offset(size.width, y = size.height - lineWidthPx),
- strokeWidth = lineWidthPx,
- )
- }
- .onInterceptKeyBeforeSoftKeyboard { keyEvent ->
- if (keyEvent.key == Key.Back) {
- viewModel.onImeDismissed()
- true
- } else {
- false
+ SelectedUserAwareInputConnection(selectedUserId) {
+ TextField(
+ value = password,
+ onValueChange = viewModel::onPasswordInputChanged,
+ enabled = isInputEnabled,
+ visualTransformation = PasswordVisualTransformation(),
+ singleLine = true,
+ textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
+ keyboardOptions =
+ KeyboardOptions(
+ keyboardType = KeyboardType.Password,
+ imeAction = ImeAction.Done,
+ ),
+ keyboardActions =
+ KeyboardActions(
+ onDone = { viewModel.onAuthenticateKeyPressed() },
+ ),
+ modifier =
+ modifier
+ .focusRequester(focusRequester)
+ .onFocusChanged { viewModel.onTextFieldFocusChanged(it.isFocused) }
+ .drawBehind {
+ drawLine(
+ color = color,
+ start = Offset(x = 0f, y = size.height - lineWidthPx),
+ end = Offset(size.width, y = size.height - lineWidthPx),
+ strokeWidth = lineWidthPx,
+ )
}
- },
- trailingIcon =
- if (isImeSwitcherButtonVisible) {
- { ImeSwitcherButton(viewModel, color) }
- } else null
- )
+ .onInterceptKeyBeforeSoftKeyboard { keyEvent ->
+ if (keyEvent.key == Key.Back) {
+ viewModel.onImeDismissed()
+ true
+ } else {
+ false
+ }
+ },
+ trailingIcon =
+ if (isImeSwitcherButtonVisible) {
+ { ImeSwitcherButton(viewModel, color) }
+ } else {
+ null
+ }
+ )
+ }
}
/** Button for changing the password input method (IME). */
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
new file mode 100644
index 000000000000..c8e145034551
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:OptIn(ExperimentalComposeUiApi::class)
+
+package com.android.systemui.common.ui.compose
+
+import android.annotation.UserIdInt
+import android.os.UserHandle
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputConnection
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.platform.InterceptPlatformTextInput
+import androidx.compose.ui.platform.PlatformTextInputMethodRequest
+
+/**
+ * Wrapper for input connection composables that need to be aware of the selected user to connect to
+ * the correct instance of on-device services like autofill, autocorrect, etc.
+ *
+ * Usage:
+ * ```
+ * @Composable
+ * fun YourFunction(viewModel: YourViewModel) {
+ * val selectedUserId by viewModel.selectedUserId.collectAsState()
+ *
+ * SelectedUserAwareInputConnection(selectedUserId) {
+ * TextField(...)
+ * }
+ * }
+ * ```
+ */
+@Composable
+fun SelectedUserAwareInputConnection(
+ @UserIdInt selectedUserId: Int,
+ content: @Composable () -> Unit,
+) {
+ InterceptPlatformTextInput(
+ interceptor = { request, nextHandler ->
+ // Create a new request to wrap the incoming one with some custom logic.
+ val modifiedRequest =
+ object : PlatformTextInputMethodRequest {
+ override fun createInputConnection(outAttributes: EditorInfo): InputConnection {
+ val inputConnection = request.createInputConnection(outAttributes)
+ // After the original request finishes initializing the EditorInfo we can
+ // customize it. If we needed to we could also wrap the InputConnection
+ // before
+ // returning it.
+ updateEditorInfo(outAttributes)
+ return inputConnection
+ }
+
+ fun updateEditorInfo(outAttributes: EditorInfo) {
+ outAttributes.targetInputMethodUser = UserHandle.of(selectedUserId)
+ }
+ }
+
+ // Send our wrapping request to the next handler, which could be the system or another
+ // interceptor up the tree.
+ nextHandler.startInputMethod(modifiedRequest)
+ }
+ ) {
+ content()
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 7535a51675e3..9ee69bc065f6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -14,7 +14,6 @@ import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.FixedSizeEdgeDetector
import com.android.compose.animation.scene.LowestZIndexScenePicker
-import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
@@ -24,14 +23,11 @@ import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.compose.animation.scene.updateSceneTransitionLayoutState
import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.res.R
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.transform
object Communal {
object Elements {
@@ -41,7 +37,7 @@ object Communal {
}
val sceneTransitions = transitions {
- to(TransitionSceneKey.Communal) {
+ to(CommunalScenes.Communal) {
spec = tween(durationMillis = 1000)
translate(Communal.Elements.Content, Edge.Right)
timestampRange(startMillis = 167, endMillis = 334) {
@@ -49,7 +45,7 @@ val sceneTransitions = transitions {
fade(Communal.Elements.Content)
}
}
- to(TransitionSceneKey.Blank) {
+ to(CommunalScenes.Blank) {
spec = tween(durationMillis = 1000)
translate(Communal.Elements.Content, Edge.Right)
timestampRange(endMillis = 167) { fade(Communal.Elements.Content) }
@@ -68,14 +64,11 @@ fun CommunalContainer(
modifier: Modifier = Modifier,
viewModel: CommunalViewModel,
) {
- val currentScene: SceneKey by
- viewModel.currentScene
- .transform { value -> emit(value.toTransitionSceneKey()) }
- .collectAsState(TransitionSceneKey.Blank)
+ val currentScene: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
val sceneTransitionLayoutState =
updateSceneTransitionLayoutState(
currentScene,
- onChangeScene = { viewModel.onSceneChanged(it.toCommunalSceneKey()) },
+ onChangeScene = { viewModel.onSceneChanged(it) },
transitions = sceneTransitions,
)
val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false)
@@ -83,9 +76,7 @@ fun CommunalContainer(
// This effect exposes the SceneTransitionLayout's observable transition state to the rest of
// the system, and unsets it when the view is disposed to avoid a memory leak.
DisposableEffect(viewModel, sceneTransitionLayoutState) {
- viewModel.setTransitionState(
- sceneTransitionLayoutState.observableTransitionState().map { it.toModel() }
- )
+ viewModel.setTransitionState(sceneTransitionLayoutState.observableTransitionState())
onDispose { viewModel.setTransitionState(null) }
}
@@ -98,11 +89,10 @@ fun CommunalContainer(
),
) {
scene(
- TransitionSceneKey.Blank,
+ CommunalScenes.Blank,
userActions =
mapOf(
- Swipe(SwipeDirection.Left, fromSource = Edge.Right) to
- TransitionSceneKey.Communal
+ Swipe(SwipeDirection.Left, fromSource = Edge.Right) to CommunalScenes.Communal
)
) {
// This scene shows nothing only allowing for transitions to the communal scene.
@@ -110,11 +100,9 @@ fun CommunalContainer(
}
scene(
- TransitionSceneKey.Communal,
+ CommunalScenes.Communal,
userActions =
- mapOf(
- Swipe(SwipeDirection.Right, fromSource = Edge.Left) to TransitionSceneKey.Blank
- ),
+ mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank),
) {
CommunalScene(viewModel, modifier = modifier)
}
@@ -135,39 +123,3 @@ private fun SceneScope.CommunalScene(
)
Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) }
}
-
-// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
-object TransitionSceneKey {
- val Blank = CommunalSceneKey.Blank.toTransitionSceneKey()
- val Communal = CommunalSceneKey.Communal.toTransitionSceneKey()
-}
-
-// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
-fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
- return this.identity as CommunalSceneKey
-}
-
-// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
-fun CommunalSceneKey.toTransitionSceneKey(): SceneKey {
- return SceneKey(debugName = toString(), identity = this)
-}
-
-/**
- * Converts between the [SceneTransitionLayout] state class and our forked data class that can be
- * used throughout SysUI.
- */
-// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
-fun ObservableTransitionState.toModel(): ObservableCommunalTransitionState {
- return when (this) {
- is ObservableTransitionState.Idle ->
- ObservableCommunalTransitionState.Idle(scene.toCommunalSceneKey())
- is ObservableTransitionState.Transition ->
- ObservableCommunalTransitionState.Transition(
- fromScene = fromScene.toCommunalSceneKey(),
- toScene = toScene.toCommunalSceneKey(),
- progress = progress,
- isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = isUserInputOngoing,
- )
- }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 11a38f92c234..3d88ad53685e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -19,12 +19,13 @@ package com.android.systemui.communal.ui.compose
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -38,12 +39,12 @@ class CommunalScene
constructor(
private val viewModel: CommunalViewModel,
) : ComposableScene {
- override val key = SceneKey.Communal
+ override val key = Scenes.Communal
override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
MutableStateFlow<Map<UserAction, UserActionResult>>(
mapOf(
- UserAction.Swipe(Direction.RIGHT) to UserActionResult(SceneKey.Lockscreen),
+ Swipe(SwipeDirection.Right) to UserActionResult(Scenes.Lockscreen),
)
)
.asStateFlow()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
index dd8664696973..a8d801abdcd0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
@@ -18,8 +18,11 @@ package com.android.systemui.keyboard.stickykeys.ui.view
import android.content.Context
import android.view.View
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -61,22 +64,32 @@ fun StickyKeysIndicator(viewModel: StickyKeysIndicatorViewModel) {
@Composable
fun StickyKeysIndicator(stickyKeys: Map<ModifierKey, Locked>, modifier: Modifier = Modifier) {
Surface(
- color = MaterialTheme.colorScheme.surface,
+ color = MaterialTheme.colorScheme.inverseSurface,
shape = MaterialTheme.shapes.medium,
- modifier = modifier
+ modifier = modifier.heightIn(min = 84.dp).width(96.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
modifier = Modifier.padding(16.dp)
) {
- stickyKeys.forEach { (key, isLocked) ->
- key(key) {
- Text(
- text = key.displayedText,
- fontWeight = if (isLocked.locked) FontWeight.Bold else FontWeight.Normal
- )
- }
- }
+ stickyKeys.forEach { (key, isLocked) -> key(key) { StickyKeyText(key, isLocked) } }
}
}
}
+
+@Composable
+private fun StickyKeyText(key: ModifierKey, isLocked: Locked, modifier: Modifier = Modifier) {
+ Text(
+ text = key.displayedText,
+ fontWeight = if (isLocked.locked) FontWeight.Bold else FontWeight.Normal,
+ style = MaterialTheme.typography.bodyMedium,
+ color =
+ if (isLocked.locked) {
+ MaterialTheme.colorScheme.inverseOnSurface
+ } else {
+ MaterialTheme.colorScheme.outlineVariant
+ },
+ modifier = modifier
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index dd043dbebaa6..a02781ba63f7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -18,19 +18,20 @@ package com.android.systemui.keyguard.ui.composable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.qs.ui.composable.QuickSettings
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.Edge
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import dagger.Lazy
import javax.inject.Inject
@@ -50,7 +51,7 @@ constructor(
viewModel: LockscreenSceneViewModel,
private val lockscreenContent: Lazy<LockscreenContent>,
) : ComposableScene {
- override val key = SceneKey.Lockscreen
+ override val key = Scenes.Lockscreen
override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
combine(viewModel.upDestinationSceneKey, viewModel.leftDestinationSceneKey, ::Pair)
@@ -80,11 +81,11 @@ constructor(
left: SceneKey?,
): Map<UserAction, UserActionResult> {
return buildMap {
- up?.let { this[UserAction.Swipe(Direction.UP)] = UserActionResult(up) }
- left?.let { this[UserAction.Swipe(Direction.LEFT)] = UserActionResult(left) }
- this[UserAction.Swipe(fromEdge = Edge.TOP, direction = Direction.DOWN)] =
- UserActionResult(SceneKey.QuickSettings)
- this[UserAction.Swipe(direction = Direction.DOWN)] = UserActionResult(SceneKey.Shade)
+ up?.let { this[Swipe(SwipeDirection.Up)] = UserActionResult(up) }
+ left?.let { this[Swipe(SwipeDirection.Left)] = UserActionResult(left) }
+ this[Swipe(fromSource = Edge.Top, direction = SwipeDirection.Down)] =
+ UserActionResult(Scenes.QuickSettings)
+ this[Swipe(direction = SwipeDirection.Down)] = UserActionResult(Scenes.Shade)
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 452dc03facfd..d23cd0c06aab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -35,11 +35,13 @@ import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSect
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -63,6 +65,7 @@ constructor(
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
) : ComposableLockscreenSceneBlueprint {
@@ -112,10 +115,16 @@ constructor(
if (viewModel.isLargeClockVisible) {
Spacer(modifier = Modifier.weight(weight = 1f))
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) {
+ LargeClock(
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 71c60c70a655..c422c4b58b55 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -35,6 +35,7 @@ import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSect
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -63,6 +64,7 @@ constructor(
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
) : ComposableLockscreenSceneBlueprint {
@@ -115,7 +117,9 @@ constructor(
with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index af836b68544c..d2184252102b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -41,6 +41,7 @@ import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSect
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -70,6 +71,7 @@ constructor(
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
) : ComposableLockscreenSceneBlueprint {
@@ -100,6 +102,14 @@ constructor(
modifier = Modifier.fillMaxHeight().weight(weight = 1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+
with(smartSpaceSection) {
SmartSpace(
burnInParams = burnIn.parameters,
@@ -121,9 +131,13 @@ constructor(
)
}
- Spacer(modifier = Modifier.weight(weight = 1f))
- with(clockSection) { LargeClock() }
- Spacer(modifier = Modifier.weight(weight = 1f))
+ if (viewModel.isLargeClockVisible) {
+ Spacer(modifier = Modifier.weight(weight = 1f))
+ with(clockSection) { LargeClock() }
+ Spacer(modifier = Modifier.weight(weight = 1f))
+ }
+
+ with(mediaCarouselSection) { MediaCarousel() }
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index e2e7a950cdfd..f86623fe935c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -41,12 +41,14 @@ import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import dagger.Binds
@@ -68,6 +70,7 @@ constructor(
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
private val clockInteractor: KeyguardClockInteractor,
+ private val mediaCarouselSection: MediaCarouselSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
@@ -107,7 +110,9 @@ constructor(
)
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
@@ -228,6 +233,7 @@ constructor(
private val clockInteractor: KeyguardClockInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val weatherClockSection: WeatherClockSection,
+ private val mediaCarouselSection: MediaCarouselSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
@@ -276,6 +282,8 @@ constructor(
),
)
}
+
+ with(mediaCarouselSection) { MediaCarousel() }
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 335c915411ee..152cc67f6c9e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -39,7 +39,6 @@ import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChange
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
/** Provides small clock and large clock composables for the default clock face. */
@@ -49,7 +48,6 @@ constructor(
private val viewModel: KeyguardClockViewModel,
private val clockInteractor: KeyguardClockInteractor,
private val aodBurnInViewModel: AodBurnInViewModel,
- private val lockscreenSmartspaceController: LockscreenSmartspaceController,
) {
@Composable
@@ -62,15 +60,11 @@ constructor(
val currentClock by viewModel.currentClock.collectAsState()
viewModel.clock = currentClock
- if (clockSize != KeyguardClockSwitch.SMALL) {
+ if (clockSize != KeyguardClockSwitch.SMALL || currentClock?.smallClock?.view == null) {
onTopChanged(null)
return
}
- if (currentClock?.smallClock?.view == null) {
- return
- }
-
val view = LocalView.current
DisposableEffect(view) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
new file mode 100644
index 000000000000..dae120cca981
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.section
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.dimensionResource
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.viewmodel.MediaCarouselViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.res.R
+import com.android.systemui.util.animation.MeasurementInput
+import javax.inject.Inject
+import javax.inject.Named
+
+class MediaCarouselSection
+@Inject
+constructor(
+ private val mediaCarouselController: MediaCarouselController,
+ @param:Named(MediaModule.KEYGUARD) private val mediaHost: MediaHost,
+ private val mediaCarouselViewModel: MediaCarouselViewModel,
+) {
+
+ @Composable
+ fun SceneScope.MediaCarousel(modifier: Modifier = Modifier) {
+ if (!mediaCarouselViewModel.isMediaVisible) {
+ return
+ }
+
+ if (mediaCarouselController.mediaFrame == null) {
+ return
+ }
+
+ val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
+ // TODO(b/312714128): MediaPlayer background size is not as expected.
+ MediaCarousel(
+ modifier =
+ modifier.height(mediaHeight).fillMaxWidth().onSizeChanged { size ->
+ // Notify controller to size the carousel for the
+ // current space
+ mediaHost.measurementInput = MeasurementInput(size.width, size.height)
+ mediaCarouselController.setSceneContainerSize(size.width, size.height)
+ },
+ mediaHost = mediaHost,
+ layoutWidth = 0, // Layout width is not used.
+ layoutHeight = with(LocalDensity.current) { mediaHeight.toPx() }.toInt(),
+ carouselController = mediaCarouselController,
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 61b2d4e26097..d3e4553be209 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -16,9 +16,12 @@
package com.android.systemui.media.controls.ui.composable
+import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.contains
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -45,6 +48,20 @@ fun SceneScope.MediaCarousel(
AndroidView(
modifier = modifier.element(MediaCarousel.Elements.Content),
- factory = { _ -> carouselController.mediaFrame },
+ factory = { context ->
+ FrameLayout(context).apply {
+ val mediaFrame = carouselController.mediaFrame
+ (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+ addView(mediaFrame)
+ }
+ },
+ update = {
+ if (it.contains(carouselController.mediaFrame)) {
+ return@AndroidView
+ }
+ val mediaFrame = carouselController.mediaFrame
+ (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+ it.addView(mediaFrame)
+ },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index ef6ae2ecfec9..791d629179e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -71,8 +71,7 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadi
import com.android.systemui.notifications.ui.composable.Notifications.Form
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
-import com.android.systemui.scene.ui.composable.Gone
-import com.android.systemui.scene.ui.composable.Shade
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationStackAppearanceViewBinder.SCRIM_CORNER_RADIUS
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
@@ -214,7 +213,7 @@ fun SceneScope.NotificationScrollingStack(
// in step with the transition so that it is 0 when it completes.
if (
scrimOffset.value < 0 &&
- layoutState.isTransitioning(from = Shade, to = Gone)
+ layoutState.isTransitioning(from = Scenes.Shade, to = Scenes.Gone)
) {
IntOffset(x = 0, y = (scrimOffset.value * expansionFraction).roundToInt())
} else {
@@ -226,7 +225,7 @@ fun SceneScope.NotificationScrollingStack(
calculateCornerRadius(
screenCornerRadius,
{ expansionFraction },
- layoutState.isTransitioningBetween(Gone, Shade)
+ layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade)
)
.let {
RoundedCornerShape(
@@ -250,7 +249,7 @@ fun SceneScope.NotificationScrollingStack(
Modifier.fillMaxSize()
.graphicsLayer {
alpha =
- if (layoutState.isTransitioningBetween(Gone, Shade)) {
+ if (layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade)) {
(expansionFraction / EXPANSION_FOR_MAX_SCRIM_ALPHA).coerceAtMost(1f)
} else 1f
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 5d0b9ba2c736..91b737d33418 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -37,14 +37,13 @@ import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Unsquishing
-import com.android.systemui.scene.ui.composable.QuickSettings as QuickSettingsSceneKey
-import com.android.systemui.scene.ui.composable.Shade
+import com.android.systemui.scene.shared.model.Scenes
object QuickSettings {
private val SCENES =
setOf(
- QuickSettingsSceneKey,
- Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
)
object Elements {
@@ -69,18 +68,20 @@ private fun SceneScope.stateForQuickSettingsContent(
return when (val transitionState = layoutState.transitionState) {
is TransitionState.Idle -> {
when (transitionState.currentScene) {
- Shade -> QSSceneAdapter.State.QQS
- QuickSettingsSceneKey -> QSSceneAdapter.State.QS
+ Scenes.Shade -> QSSceneAdapter.State.QQS
+ Scenes.QuickSettings -> QSSceneAdapter.State.QS
else -> QSSceneAdapter.State.CLOSED
}
}
is TransitionState.Transition ->
with(transitionState) {
when {
- fromScene == Shade && toScene == QuickSettingsSceneKey -> Expanding(progress)
- fromScene == QuickSettingsSceneKey && toScene == Shade -> Collapsing(progress)
- fromScene == Shade || toScene == Shade -> Unsquishing(squishiness)
- fromScene == QuickSettingsSceneKey || toScene == QuickSettingsSceneKey -> {
+ fromScene == Scenes.Shade && toScene == Scenes.QuickSettings ->
+ Expanding(progress)
+ fromScene == Scenes.QuickSettings && toScene == Scenes.Shade ->
+ Collapsing(progress)
+ fromScene == Scenes.Shade || toScene == Scenes.Shade -> Unsquishing(squishiness)
+ fromScene == Scenes.QuickSettings || toScene == Scenes.QuickSettings -> {
QSSceneAdapter.State.QS
}
else ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 6875bc544a55..3b8b863fdde2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -63,9 +63,8 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.qs.footer.ui.compose.FooterActions
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
-import com.android.systemui.scene.ui.composable.asComposeAware
import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.Shade
@@ -89,7 +88,7 @@ constructor(
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
) : ComposableScene {
- override val key = SceneKey.QuickSettings
+ override val key = Scenes.QuickSettings
override val destinationScenes =
viewModel.destinationScenes.stateIn(
@@ -140,9 +139,7 @@ private fun SceneScope.QuickSettingsScene(
val isScrollable =
when (val state = layoutState.transitionState) {
is TransitionState.Idle -> true
- is TransitionState.Transition -> {
- state.fromScene == SceneKey.QuickSettings.asComposeAware()
- }
+ is TransitionState.Transition -> state.fromScene == Scenes.QuickSettings
}
LaunchedEffect(isCustomizing, scrollState) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt
deleted file mode 100644
index 0de4650f1248..000000000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.ui.composable
-
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Edge as ComposeAwareEdge
-import com.android.compose.animation.scene.SceneKey as ComposeAwareSceneKey
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.TransitionKey as ComposeAwareTransitionKey
-import com.android.compose.animation.scene.UserAction as ComposeAwareUserAction
-import com.android.compose.animation.scene.UserActionResult as ComposeAwareUserActionResult
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.Edge
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.TransitionKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
-
-// TODO(b/293899074): remove this file once we can use the types from SceneTransitionLayout.
-
-fun SceneKey.asComposeAware(): ComposeAwareSceneKey {
- return ComposeAwareSceneKey(
- debugName = toString(),
- identity = this,
- )
-}
-
-fun TransitionKey.asComposeAware(): ComposeAwareTransitionKey {
- return ComposeAwareTransitionKey(
- debugName = debugName,
- identity = this,
- )
-}
-
-fun UserAction.asComposeAware(): ComposeAwareUserAction {
- return when (this) {
- is UserAction.Swipe ->
- Swipe(
- pointerCount = pointerCount,
- fromSource =
- when (this.fromEdge) {
- null -> null
- Edge.LEFT -> ComposeAwareEdge.Left
- Edge.TOP -> ComposeAwareEdge.Top
- Edge.RIGHT -> ComposeAwareEdge.Right
- Edge.BOTTOM -> ComposeAwareEdge.Bottom
- },
- direction =
- when (this.direction) {
- Direction.LEFT -> SwipeDirection.Left
- Direction.UP -> SwipeDirection.Up
- Direction.RIGHT -> SwipeDirection.Right
- Direction.DOWN -> SwipeDirection.Down
- }
- )
- is UserAction.Back -> Back
- }
-}
-
-fun UserActionResult.asComposeAware(): ComposeAwareUserActionResult {
- val composeUnaware = this
- return ComposeAwareUserActionResult(
- toScene = composeUnaware.toScene.asComposeAware(),
- transitionKey = composeUnaware.transitionKey?.asComposeAware(),
- )
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeUnawareExtensions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeUnawareExtensions.kt
deleted file mode 100644
index 4c03664fc244..000000000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeUnawareExtensions.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.ui.composable
-
-import com.android.compose.animation.scene.ObservableTransitionState as ComposeAwareObservableTransitionState
-import com.android.compose.animation.scene.SceneKey as ComposeAwareSceneKey
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
-
-fun ComposeAwareSceneKey.asComposeUnaware(): SceneKey {
- return this.identity as SceneKey
-}
-
-fun ComposeAwareObservableTransitionState.asComposeUnaware(): ObservableTransitionState {
- return when (this) {
- is ComposeAwareObservableTransitionState.Idle ->
- ObservableTransitionState.Idle(scene.asComposeUnaware())
- is ComposeAwareObservableTransitionState.Transition ->
- ObservableTransitionState.Transition(
- fromScene = fromScene.asComposeUnaware(),
- toScene = toScene.asComposeUnaware(),
- progress = progress,
- isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = isUserInputOngoing,
- )
- }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 9ca751e81eed..82f56abbded6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -19,17 +19,17 @@ package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.ui.composable.QuickSettings
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.Edge
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -46,18 +46,17 @@ class GoneScene
constructor(
private val notificationsViewModel: NotificationsPlaceholderViewModel,
) : ComposableScene {
- override val key = SceneKey.Gone
+ override val key = Scenes.Gone
override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
MutableStateFlow<Map<UserAction, UserActionResult>>(
mapOf(
- UserAction.Swipe(
+ Swipe(
pointerCount = 2,
- fromEdge = Edge.TOP,
- direction = Direction.DOWN,
- ) to UserActionResult(SceneKey.QuickSettings),
- UserAction.Swipe(direction = Direction.DOWN) to
- UserActionResult(SceneKey.Shade),
+ fromSource = Edge.Top,
+ direction = SwipeDirection.Down,
+ ) to UserActionResult(Scenes.QuickSettings),
+ Swipe(direction = SwipeDirection.Down) to UserActionResult(Scenes.Shade),
)
)
.asStateFlow()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 9779d7170d0d..0fdaabe75306 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -35,15 +35,14 @@ import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.motionEventSpy
import androidx.compose.ui.input.pointer.pointerInput
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.observableTransitionState
import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import kotlinx.coroutines.flow.map
/**
* Renders a container of a collection of "scenes" that the user can switch between using certain
@@ -77,8 +76,8 @@ fun SceneContainer(
currentScene.destinationScenes.collectAsState()
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
- initialScene = currentSceneKey.asComposeAware(),
- canChangeScene = { toScene -> viewModel.canChangeScene(toScene.asComposeUnaware()) },
+ initialScene = currentSceneKey,
+ canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
transitions = SceneContainerTransitions,
)
}
@@ -90,9 +89,7 @@ fun SceneContainer(
}
DisposableEffect(viewModel, state) {
- viewModel.setTransitionState(
- state.observableTransitionState().map { it.asComposeUnaware() }
- )
+ viewModel.setTransitionState(state.observableTransitionState())
onDispose { viewModel.setTransitionState(null) }
}
@@ -116,23 +113,17 @@ fun SceneContainer(
) {
sceneByKey.forEach { (sceneKey, composableScene) ->
scene(
- key = sceneKey.asComposeAware(),
+ key = sceneKey,
userActions =
if (sceneKey == currentSceneKey) {
- currentDestinations
- } else {
- composableScene.destinationScenes.value
- }
- .map { (userAction, userActionResult) ->
- userAction.asComposeAware() to userActionResult.asComposeAware()
- }
- .toMap(),
+ currentDestinations
+ } else {
+ composableScene.destinationScenes.value
+ },
) {
with(composableScene) {
this@scene.Content(
- modifier =
- Modifier.element(sceneKey.asComposeAware().rootElementKey)
- .fillMaxSize(),
+ modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 61f81209ad7e..dea9485e916c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -1,6 +1,7 @@
package com.android.systemui.scene.ui.composable
import com.android.compose.animation.scene.transitions
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys.CollapseShadeInstantly
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
@@ -26,41 +27,41 @@ import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettings
* Please keep the list sorted alphabetically.
*/
val SceneContainerTransitions = transitions {
- from(Bouncer, to = Gone) { bouncerToGoneTransition() }
- from(Gone, to = Shade) { goneToShadeTransition() }
+ from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
+ from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
from(
- Gone,
- to = Shade,
- key = CollapseShadeInstantly.asComposeAware(),
+ Scenes.Gone,
+ to = Scenes.Shade,
+ key = CollapseShadeInstantly,
) {
goneToShadeTransition(durationScale = 0.0)
}
from(
- Gone,
- to = Shade,
- key = SlightlyFasterShadeCollapse.asComposeAware(),
+ Scenes.Gone,
+ to = Scenes.Shade,
+ key = SlightlyFasterShadeCollapse,
) {
goneToShadeTransition(durationScale = 0.9)
}
- from(Gone, to = QuickSettings) { goneToQuickSettingsTransition() }
- from(Lockscreen, to = Bouncer) { lockscreenToBouncerTransition() }
- from(Lockscreen, to = Communal) { lockscreenToCommunalTransition() }
- from(Lockscreen, to = Shade) { lockscreenToShadeTransition() }
+ from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() }
+ from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() }
+ from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
+ from(Scenes.Lockscreen, to = Scenes.Shade) { lockscreenToShadeTransition() }
from(
- Lockscreen,
- to = Shade,
- key = CollapseShadeInstantly.asComposeAware(),
+ Scenes.Lockscreen,
+ to = Scenes.Shade,
+ key = CollapseShadeInstantly,
) {
lockscreenToShadeTransition(durationScale = 0.0)
}
from(
- Lockscreen,
- to = Shade,
- key = SlightlyFasterShadeCollapse.asComposeAware(),
+ Scenes.Lockscreen,
+ to = Scenes.Shade,
+ key = SlightlyFasterShadeCollapse,
) {
lockscreenToShadeTransition(durationScale = 0.9)
}
- from(Lockscreen, to = QuickSettings) { lockscreenToQuickSettingsTransition() }
- from(Lockscreen, to = Gone) { lockscreenToGoneTransition() }
- from(Shade, to = QuickSettings) { shadeToQuickSettingsTransition() }
+ from(Scenes.Lockscreen, to = Scenes.QuickSettings) { lockscreenToQuickSettingsTransition() }
+ from(Scenes.Lockscreen, to = Scenes.Gone) { lockscreenToGoneTransition() }
+ from(Scenes.Shade, to = Scenes.QuickSettings) { shadeToQuickSettingsTransition() }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
index 60c0b7719a25..a54994df3dc9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
@@ -20,10 +20,10 @@ package com.android.systemui.scene.ui.composable
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.observableTransitionState
import com.android.systemui.scene.shared.model.SceneDataSource
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.TransitionKey
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
@@ -61,11 +61,10 @@ class SceneTransitionLayoutDataSource(
}
}
}
- .map { it.asComposeUnaware() }
.stateIn(
scope = coroutineScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = state.transitionState.currentScene.asComposeUnaware(),
+ initialValue = state.transitionState.currentScene,
)
override fun changeScene(
@@ -73,8 +72,8 @@ class SceneTransitionLayoutDataSource(
transitionKey: TransitionKey?,
) {
state.setTargetScene(
- targetScene = toScene.asComposeAware(),
- transitionKey = transitionKey?.asComposeAware(),
+ targetScene = toScene,
+ transitionKey = transitionKey,
coroutineScope = coroutineScope,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
deleted file mode 100644
index 5a9add1ad587..000000000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.android.systemui.scene.ui.composable
-
-import com.android.systemui.scene.shared.model.SceneKey
-
-val Lockscreen = SceneKey.Lockscreen.asComposeAware()
-val Bouncer = SceneKey.Bouncer.asComposeAware()
-val Shade = SceneKey.Shade.asComposeAware()
-val QuickSettings = SceneKey.QuickSettings.asComposeAware()
-val Gone = SceneKey.Gone.asComposeAware()
-val Communal = SceneKey.Communal.asComposeAware()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt
index 1a9facea7518..5eefe490ab5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromBouncerToGoneTransition.kt
@@ -2,10 +2,10 @@ package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.TransitionBuilder
-import com.android.systemui.scene.ui.composable.Bouncer
+import com.android.systemui.scene.shared.model.Scenes
fun TransitionBuilder.bouncerToGoneTransition() {
spec = tween(durationMillis = 500)
- fade(Bouncer.rootElementKey)
+ fade(Scenes.Bouncer.rootElementKey)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt
index 291617f8edde..5bd158349f5e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsTransition.kt
@@ -3,10 +3,10 @@ package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
-import com.android.systemui.scene.ui.composable.QuickSettings
+import com.android.systemui.scene.shared.model.Scenes
fun TransitionBuilder.goneToQuickSettingsTransition() {
spec = tween(durationMillis = 500)
- translate(QuickSettings.rootElementKey, Edge.Top, true)
+ translate(Scenes.QuickSettings.rootElementKey, Edge.Top, true)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
index ea8110ad8518..0021bf59d875 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -19,15 +19,14 @@ package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
-import com.android.systemui.scene.ui.composable.Communal
-import com.android.systemui.scene.ui.composable.Lockscreen
+import com.android.systemui.scene.shared.model.Scenes
fun TransitionBuilder.lockscreenToCommunalTransition() {
spec = tween(durationMillis = 500)
// Translate lockscreen to the left.
- translate(Lockscreen.rootElementKey, Edge.Left)
+ translate(Scenes.Lockscreen.rootElementKey, Edge.Left)
// Translate communal from the right.
- translate(Communal.rootElementKey, Edge.Right)
+ translate(Scenes.Communal.rootElementKey, Edge.Right)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt
index da6306dc656d..3e576bc9d538 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToGoneTransition.kt
@@ -2,10 +2,10 @@ package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.TransitionBuilder
-import com.android.systemui.scene.ui.composable.Lockscreen
+import com.android.systemui.scene.shared.model.Scenes
fun TransitionBuilder.lockscreenToGoneTransition() {
spec = tween(durationMillis = 500)
- fade(Lockscreen.rootElementKey)
+ fade(Scenes.Lockscreen.rootElementKey)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt
index e63bc4e458eb..962d8227a016 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsTransition.kt
@@ -3,10 +3,10 @@ package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
-import com.android.systemui.scene.ui.composable.QuickSettings
+import com.android.systemui.scene.shared.model.Scenes
fun TransitionBuilder.lockscreenToQuickSettingsTransition() {
spec = tween(durationMillis = 500)
- translate(QuickSettings.rootElementKey, Edge.Top, true)
+ translate(Scenes.QuickSettings.rootElementKey, Edge.Top, true)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index d7911eac8a61..12b07a3a69c2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -63,8 +63,7 @@ import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
-import com.android.systemui.scene.ui.composable.QuickSettings
-import com.android.systemui.scene.ui.composable.Shade as ShadeKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@@ -443,7 +442,7 @@ private fun SceneScope.StatusIcons(
},
update = { iconContainer ->
iconContainer.setQsExpansionTransitioning(
- layoutState.isTransitioningBetween(ShadeKey, QuickSettings)
+ layoutState.isTransitioningBetween(Scenes.Shade, Scenes.QuickSettings)
)
if (isSingleCarrier || !useExpandedFormat) {
iconContainer.removeIgnoredSlots(carrierIconSlots)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 8484b7f5273f..3620cc570c66 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -41,7 +41,12 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.modifiers.thenIf
import com.android.systemui.battery.BatteryMeterViewController
@@ -56,10 +61,7 @@ import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.statusbar.phone.StatusBarIconController
@@ -109,7 +111,7 @@ constructor(
private val mediaCarouselController: MediaCarouselController,
@Named(QUICK_QS_PANEL) private val mediaHost: MediaHost,
) : ComposableScene {
- override val key = SceneKey.Shade
+ override val key = Scenes.Shade
override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
viewModel.upDestinationSceneKey
@@ -144,8 +146,8 @@ constructor(
up: SceneKey,
): Map<UserAction, UserActionResult> {
return mapOf(
- UserAction.Swipe(Direction.UP) to UserActionResult(up),
- UserAction.Swipe(Direction.DOWN) to UserActionResult(SceneKey.QuickSettings),
+ Swipe(SwipeDirection.Up) to UserActionResult(up),
+ Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index b3fcc305e6b5..53de5bc3a36d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -27,12 +27,14 @@ import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
@@ -137,12 +139,14 @@ constructor(
}
}
) { targetViewModel ->
- Expandable(
- modifier = Modifier.fillMaxSize(),
- color = targetViewModel.backgroundColor.toColor(),
- shape = RoundedCornerShape(12.dp),
- onClick = { viewModel.onDeviceClick(it) },
- ) {}
+ Spacer(
+ modifier =
+ Modifier.fillMaxSize()
+ .background(
+ color = targetViewModel.backgroundColor.toColor(),
+ shape = RoundedCornerShape(12.dp),
+ ),
+ )
}
transition.AnimatedContent(
contentKey = { it.icon },
diff --git a/packages/SystemUI/compose/features/tests/AndroidManifest.xml b/packages/SystemUI/compose/features/tests/AndroidManifest.xml
deleted file mode 100644
index fc337fb19f43..000000000000
--- a/packages/SystemUI/compose/features/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.systemui.compose.features.tests" >
-
- <application
- android:name="android.app.Application"
- android:appComponentFactory="androidx.core.app.AppComponentFactory"
- tools:replace="android:name,android:appComponentFactory">
- <uses-library android:name="android.test.runner" />
-
- <!-- Disable providers from SystemUI -->
- <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
- android:authorities="com.android.systemui.test.keyguard.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove" />
- <provider android:name="com.android.systemui.keyguard.CustomizationProvider"
- android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove" />
- <provider android:name="com.android.systemui.people.PeopleProvider"
- android:authorities="com.android.systemui.test.people.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove" />
- <provider android:name="androidx.core.content.FileProvider"
- android:authorities="com.android.systemui.test.fileprovider.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove"/>
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.systemui.compose.features.tests"
- android:label="Tests for SystemUIComposeFeatures"/>
-
-</manifest> \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/TEST_MAPPING b/packages/SystemUI/compose/scene/TEST_MAPPING
index f644a23ba0a3..f9424ed62d78 100644
--- a/packages/SystemUI/compose/scene/TEST_MAPPING
+++ b/packages/SystemUI/compose/scene/TEST_MAPPING
@@ -23,17 +23,6 @@
]
},
{
- "name": "SystemUIComposeFeaturesTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "SystemUIComposeGalleryTests",
"options": [
{
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 1e3842a1de68..b7e2dd13f321 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -391,7 +391,7 @@ interface SwipeSourceDetector {
}
/** The result of performing a [UserAction]. */
-class UserActionResult(
+data class UserActionResult(
/** The scene we should be transitioning to during the [UserAction]. */
val toScene: SceneKey,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index c3857889a134..7707a600c691 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -17,6 +17,8 @@
package com.android.keyguard
import android.testing.TestableLooper
+import android.view.KeyEvent
+import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
@@ -25,8 +27,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor
-import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -36,12 +38,17 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
@@ -49,6 +56,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import com.android.systemui.Flags as AconfigFlags
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -77,6 +85,7 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock private lateinit var postureController: DevicePostureController
+ @Captor private lateinit var keyListenerArgumentCaptor: ArgumentCaptor<View.OnKeyListener>
private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -171,4 +180,34 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
keyguardPasswordViewController.resetState()
verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password)
}
+
+ @Test
+ fun testSpaceKeyDoesNotSubmitPassword() {
+ keyguardPasswordViewController.onViewAttached()
+ verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture())
+
+ val eventHandled =
+ keyListenerArgumentCaptor.value.onKey(keyguardPasswordView,
+ KeyEvent.KEYCODE_SPACE,
+ KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE))
+
+ assertFalse("Unlock attempted.", eventHandled)
+ }
+
+ @Test
+ fun testEnterKeySubmitsPassword() {
+ val password = mock<LockscreenCredential>()
+ `when`(keyguardPasswordView.enteredCredential).thenReturn(password)
+ `when`(password.size()).thenReturn(4)
+ `when`(password.duplicate()).thenReturn(password)
+ keyguardPasswordViewController.onViewAttached()
+ verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture())
+
+ val eventHandled =
+ keyListenerArgumentCaptor.value.onKey(keyguardPasswordView,
+ KeyEvent.KEYCODE_ENTER,
+ KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
+
+ assertTrue("Unlock not attempted.", eventHandled)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 38dc24ed2f5f..9dbeeda42986 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -31,6 +31,7 @@ import android.view.WindowInsetsController
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.internal.widget.LockPatternUtils
@@ -65,8 +66,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.FakeSceneDataSource
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -244,7 +244,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
sceneInteractor = kosmos.sceneInteractor
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
sceneTransitionStateFlow =
- MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen))
+ MutableStateFlow(ObservableTransitionState.Idle(Scenes.Lockscreen))
sceneInteractor.setTransitionState(sceneTransitionStateFlow)
deviceEntryInteractor = kosmos.deviceEntryInteractor
@@ -815,18 +815,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
// not enough to trigger a dismissal of the keyguard.
underTest.onViewAttached()
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Bouncer, "reason")
+ sceneInteractor.changeScene(Scenes.Bouncer, "reason")
sceneTransitionStateFlow.value =
ObservableTransitionState.Transition(
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
flowOf(.5f),
false,
isUserInputOngoing = flowOf(false),
)
runCurrent()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Bouncer)
- sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer)
+ sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer)
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyInt())
@@ -835,18 +835,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
runCurrent()
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
sceneTransitionStateFlow.value =
ObservableTransitionState.Transition(
- SceneKey.Bouncer,
- SceneKey.Gone,
+ Scenes.Bouncer,
+ Scenes.Gone,
flowOf(.5f),
false,
isUserInputOngoing = flowOf(false),
)
runCurrent()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone)
- sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
+ sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
runCurrent()
verify(viewMediatorCallback).keyguardDone(anyInt())
@@ -854,18 +854,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
// again.
clearInvocations(viewMediatorCallback)
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Bouncer, "reason")
+ sceneInteractor.changeScene(Scenes.Bouncer, "reason")
sceneTransitionStateFlow.value =
ObservableTransitionState.Transition(
- SceneKey.Gone,
- SceneKey.Bouncer,
+ Scenes.Gone,
+ Scenes.Bouncer,
flowOf(.5f),
false,
isUserInputOngoing = flowOf(false),
)
runCurrent()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Bouncer)
- sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Bouncer)
+ sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Bouncer)
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyInt())
@@ -874,35 +874,35 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
// does not dismiss the keyguard while we're not listening.
underTest.onViewDetached()
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
sceneTransitionStateFlow.value =
ObservableTransitionState.Transition(
- SceneKey.Bouncer,
- SceneKey.Gone,
+ Scenes.Bouncer,
+ Scenes.Gone,
flowOf(.5f),
false,
isUserInputOngoing = flowOf(false),
)
runCurrent()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone)
- sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
+ sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyInt())
// While not listening, moving to the lockscreen does not dismiss the keyguard.
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Lockscreen, "reason")
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
sceneTransitionStateFlow.value =
ObservableTransitionState.Transition(
- SceneKey.Gone,
- SceneKey.Lockscreen,
+ Scenes.Gone,
+ Scenes.Lockscreen,
flowOf(.5f),
false,
isUserInputOngoing = flowOf(false),
)
runCurrent()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Lockscreen)
- sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Lockscreen)
+ sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyInt())
@@ -910,18 +910,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
// gone scene now does dismiss the keyguard again, this time from lockscreen.
underTest.onViewAttached()
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
sceneTransitionStateFlow.value =
ObservableTransitionState.Transition(
- SceneKey.Lockscreen,
- SceneKey.Gone,
+ Scenes.Lockscreen,
+ Scenes.Gone,
flowOf(.5f),
false,
isUserInputOngoing = flowOf(false),
)
runCurrent()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone)
- sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
+ sceneTransitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
runCurrent()
verify(viewMediatorCallback).keyguardDone(anyInt())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 92eb8f8c36c2..4950b96b077f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -45,32 +45,32 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -102,6 +102,7 @@ private const val SENSOR_HEIGHT = 60
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class UdfpsControllerOverlayTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
@JvmField @Rule var rule = MockitoJUnit.rule()
@@ -148,28 +149,11 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Before
fun setup() {
- testScope = TestScope(StandardTestDispatcher())
- powerRepository = FakePowerRepository()
- powerInteractor =
- PowerInteractor(
- powerRepository,
- mock(FalsingCollector::class.java),
- mock(ScreenOffAnimationController::class.java),
- statusBarStateController,
- )
- keyguardTransitionRepository = FakeKeyguardTransitionRepository()
- keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- scope = testScope.backgroundScope,
- repository = keyguardTransitionRepository,
- fromLockscreenTransitionInteractor = {
- mock(FromLockscreenTransitionInteractor::class.java)
- },
- fromPrimaryBouncerTransitionInteractor = {
- mock(FromPrimaryBouncerTransitionInteractor::class.java)
- },
- fromAodTransitionInteractor = { mock(FromAodTransitionInteractor::class.java) },
- )
+ testScope = kosmos.testScope
+ powerRepository = kosmos.fakePowerRepository
+ powerInteractor = kosmos.powerInteractor
+ keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView)
whenever(inflater.inflate(R.layout.udfps_bp_view, null))
.thenReturn(mock(UdfpsBpView::class.java))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index ad29e68f1bbf..df50eb64f8b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.bouncer.ui.viewmodel
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
@@ -33,7 +34,7 @@ import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
@@ -93,7 +94,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
assertThat(password).isEmpty()
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Password)
}
@@ -125,7 +126,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(password).isEqualTo("password")
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -163,7 +164,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.Password
)
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- switchToScene(SceneKey.Bouncer)
+ switchToScene(Scenes.Bouncer)
// No input entered.
@@ -209,14 +210,14 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(password).isEqualTo("password")
// The user doesn't confirm the password, but navigates back to the lockscreen instead.
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
// The user navigates to the bouncer again.
- switchToScene(SceneKey.Bouncer)
+ switchToScene(Scenes.Bouncer)
// Ensure the previously-entered password is not shown.
assertThat(password).isEmpty()
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -330,8 +331,8 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.currentScene)
- val bouncerShown = currentScene != SceneKey.Bouncer && toScene == SceneKey.Bouncer
- val bouncerHidden = currentScene == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ val bouncerShown = currentScene != Scenes.Bouncer && toScene == Scenes.Bouncer
+ val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
sceneInteractor.changeScene(toScene, "reason")
if (bouncerShown) underTest.onShown()
if (bouncerHidden) underTest.onHidden()
@@ -345,7 +346,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.Password
)
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- switchToScene(SceneKey.Bouncer)
+ switchToScene(Scenes.Bouncer)
}
private suspend fun TestScope.setLockout(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 32de1f2a892c..91a056ddd685 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.authenticationRepository
@@ -31,7 +32,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -86,7 +87,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEqualTo(ENTER_YOUR_PATTERN)
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pattern)
}
@@ -104,7 +105,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -159,7 +160,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(message?.text).isEqualTo(WRONG_PATTERN)
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -369,8 +370,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.currentScene)
- val bouncerShown = currentScene != SceneKey.Bouncer && toScene == SceneKey.Bouncer
- val bouncerHidden = currentScene == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ val bouncerShown = currentScene != Scenes.Bouncer && toScene == Scenes.Bouncer
+ val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
sceneInteractor.changeScene(toScene, "reason")
if (bouncerShown) underTest.onShown()
if (bouncerHidden) underTest.onHidden()
@@ -384,7 +385,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.Pattern
)
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- switchToScene(SceneKey.Bouncer)
+ switchToScene(Scenes.Bouncer)
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index ccf7094e2bf7..7b75a3715415 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -31,7 +32,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -196,7 +197,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -230,7 +231,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(pin).isEmpty()
assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN)
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -290,7 +291,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(pin).isEmpty()
assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN)
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -304,10 +305,10 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(pin).isNotEmpty()
// The user doesn't confirm the PIN, but navigates back to the lockscreen instead.
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
// The user navigates to the bouncer again.
- switchToScene(SceneKey.Bouncer)
+ switchToScene(Scenes.Bouncer)
// Ensure the previously-entered PIN is not shown.
assertThat(pin).isEmpty()
@@ -389,8 +390,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.currentScene)
- val bouncerShown = currentScene != SceneKey.Bouncer && toScene == SceneKey.Bouncer
- val bouncerHidden = currentScene == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ val bouncerShown = currentScene != Scenes.Bouncer && toScene == Scenes.Bouncer
+ val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
sceneInteractor.changeScene(toScene, "reason")
if (bouncerShown) underTest.onShown()
if (bouncerHidden) underTest.onHidden()
@@ -402,7 +403,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
private fun TestScope.lockDeviceAndOpenPinBouncer() {
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- switchToScene(SceneKey.Bouncer)
+ switchToScene(Scenes.Bouncer)
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
new file mode 100644
index 000000000000..9cfa57257053
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.os.UserHandle
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.util.settings.fakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@android.platform.test.annotations.EnabledOnRavenwood
+class CameraAutoRotateRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+ private val testUser = UserHandle.of(1)
+
+ private val underTest =
+ CameraAutoRotateRepositoryImpl(settings, testScope.testScheduler, testScope.backgroundScope)
+
+ /** 3 changes => 3 change signals + 1 signal emitted at start => 4 signals */
+ @Test
+ fun isCameraAutoRotateSettingEnabled_3times() =
+ testScope.runTest {
+ settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier)
+ val isCameraAutoRotateSettingEnabled by
+ collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser))
+ runCurrent()
+ assertThat(isCameraAutoRotateSettingEnabled.last()).isFalse()
+
+ settings.putIntForUser(SETTING_NAME, ENABLE, testUser.identifier)
+ runCurrent()
+ assertThat(isCameraAutoRotateSettingEnabled.last()).isTrue()
+
+ settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier)
+ runCurrent()
+ assertThat(isCameraAutoRotateSettingEnabled.last()).isFalse()
+
+ settings.putIntForUser(SETTING_NAME, ENABLE, testUser.identifier)
+ runCurrent()
+ assertThat(isCameraAutoRotateSettingEnabled.last()).isTrue()
+
+ assertThat(isCameraAutoRotateSettingEnabled).hasSize(4)
+ }
+
+ @Test
+ fun isCameraAutoRotateSettingEnabled_emitsOnStart() =
+ testScope.runTest {
+ val isCameraAutoRotateSettingEnabled: List<Boolean> by
+ collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser))
+
+ runCurrent()
+
+ assertThat(isCameraAutoRotateSettingEnabled).hasSize(1)
+ }
+
+ /** 0 for 0 changes + 1 signal emitted on start => 1 signal */
+ @Test
+ fun isCameraAutoRotateSettingEnabled_0Times() =
+ testScope.runTest {
+ settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier)
+ val isCameraAutoRotateSettingEnabled: List<Boolean> by
+ collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser))
+ runCurrent()
+
+ settings.putIntForUser(SETTING_NAME, DISABLE, testUser.identifier)
+ runCurrent()
+
+ assertThat(isCameraAutoRotateSettingEnabled).hasSize(1)
+ assertThat(isCameraAutoRotateSettingEnabled[0]).isFalse()
+ }
+
+ /** Maintain that flows are cached by user */
+ @Test
+ fun sameUserCallsIsCameraAutoRotateSettingEnabledTwice_getsSameFlow() =
+ testScope.runTest {
+ val flow1 = underTest.isCameraAutoRotateSettingEnabled(testUser)
+ val flow2 = underTest.isCameraAutoRotateSettingEnabled(testUser)
+
+ assertThat(flow1).isEqualTo(flow2)
+ }
+
+ @Test
+ fun differentUsersCallIsCameraAutoRotateSettingEnabled_getDifferentFlow() =
+ testScope.runTest {
+ val user2 = UserHandle.of(2)
+ val flow1 = underTest.isCameraAutoRotateSettingEnabled(testUser)
+ val flow2 = underTest.isCameraAutoRotateSettingEnabled(user2)
+
+ assertThat(flow1).isNotEqualTo(flow2)
+ }
+
+ private companion object {
+ private const val SETTING_NAME = Settings.Secure.CAMERA_AUTOROTATE
+ private const val DISABLE = 0
+ private const val ENABLE = 1
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
new file mode 100644
index 000000000000..29de58e2b28f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.hardware.SensorPrivacyManager
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@android.platform.test.annotations.EnabledOnRavenwood
+class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val testUser = UserHandle.of(1)
+ private val privacyManager = mock<SensorPrivacyManager>()
+ private val underTest =
+ CameraSensorPrivacyRepositoryImpl(
+ testScope.testScheduler,
+ testScope.backgroundScope,
+ privacyManager
+ )
+
+ @Test
+ fun isEnabled_2TimesForSameUserReturnsCachedFlow() =
+ testScope.runTest {
+ val flow1 = underTest.isEnabled(testUser)
+ val flow2 = underTest.isEnabled(testUser)
+ runCurrent()
+
+ assertThat(flow1).isEqualTo(flow2)
+ }
+
+ @Test
+ fun isEnabled_2TimesForDifferentUsersReturnsTwoDifferentFlows() =
+ testScope.runTest {
+ val user2 = UserHandle.of(2)
+
+ val flow1 = underTest.isEnabled(testUser)
+ val flow2 = underTest.isEnabled(user2)
+ runCurrent()
+
+ assertThat(flow1).isNotEqualTo(flow2)
+ }
+
+ @Test
+ fun isEnabled_dataMatchesSensorPrivacyManager() =
+ testScope.runTest {
+ val isEnabled = collectLastValue(underTest.isEnabled(testUser))
+
+ val captor =
+ ArgumentCaptor.forClass(
+ SensorPrivacyManager.OnSensorPrivacyChangedListener::class.java
+ )
+ runCurrent()
+ assertThat(isEnabled()).isEqualTo(false)
+
+ Mockito.verify(privacyManager)
+ .addSensorPrivacyListener(
+ ArgumentMatchers.eq(SensorPrivacyManager.Sensors.CAMERA),
+ ArgumentMatchers.eq(testUser.identifier),
+ captor.capture()
+ )
+ val sensorPrivacyCallback = captor.value!!
+
+ sensorPrivacyCallback.onSensorPrivacyChanged(SensorPrivacyManager.Sensors.CAMERA, true)
+ runCurrent()
+ assertThat(isEnabled()).isEqualTo(true)
+
+ sensorPrivacyCallback.onSensorPrivacyChanged(SensorPrivacyManager.Sensors.CAMERA, false)
+ runCurrent()
+ assertThat(isEnabled()).isEqualTo(false)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
new file mode 100644
index 000000000000..f75e036d78d4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@android.platform.test.annotations.EnabledOnRavenwood
+class FakeCameraAutoRotateRepositoryTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val underTest = kosmos.fakeCameraAutoRotateRepository
+ private val testUser = UserHandle.of(1)
+
+ @Test
+ fun isCameraAutoRotateSettingEnabled_emitsFalseOnStart() = runTest {
+ val isCameraAutoRotateSettingEnabled by
+ collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser))
+
+ assertThat(isCameraAutoRotateSettingEnabled).hasSize(1)
+ assertThat(isCameraAutoRotateSettingEnabled.first()).isFalse()
+ }
+
+ /**
+ * The value explicitly set in this test is not distinct, therefore only 1 value is collected.
+ */
+ @Test
+ fun isCameraAutoRotateSettingEnabled_emitsDistinctValueOnly() = runTest {
+ val isCameraAutoRotateSettingEnabled by
+ collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser))
+ underTest.setEnabled(testUser, false)
+ runCurrent()
+
+ assertThat(isCameraAutoRotateSettingEnabled).hasSize(1)
+ assertThat(isCameraAutoRotateSettingEnabled.first()).isFalse()
+ }
+
+ @Test
+ fun isCameraAutoRotateSettingEnabled_canSetValue3Times() = runTest {
+ val isCameraAutoRotateSettingEnabled by
+ collectValues(underTest.isCameraAutoRotateSettingEnabled(testUser))
+ runCurrent()
+ underTest.setEnabled(testUser, true)
+ runCurrent()
+ underTest.setEnabled(testUser, false)
+ runCurrent()
+ underTest.setEnabled(testUser, true)
+ runCurrent()
+ assertThat(isCameraAutoRotateSettingEnabled).hasSize(4)
+ assertThat(isCameraAutoRotateSettingEnabled.last()).isTrue()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
new file mode 100644
index 000000000000..7fa1be3d20ff
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@android.platform.test.annotations.EnabledOnRavenwood
+class FakeCameraSensorPrivacyRepositoryTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val underTest = kosmos.fakeCameraSensorPrivacyRepository
+ private val testUser = UserHandle.of(1)
+
+ @Test
+ fun isCameraSensorPrivacyEnabled_emitsFalseOnStart() = runTest {
+ val isCameraSensorPrivacySettingEnabled by collectValues(underTest.isEnabled(testUser))
+
+ assertThat(isCameraSensorPrivacySettingEnabled).hasSize(1)
+ assertThat(isCameraSensorPrivacySettingEnabled.first()).isFalse()
+ }
+
+ /**
+ * The value explicitly set in this test is not distinct, therefore only 1 value is collected.
+ */
+ @Test
+ fun isCameraSensorPrivacyEnabled_emitsDistinctValueOnly() = runTest {
+ val isCameraSensorPrivacySettingEnabled by collectValues(underTest.isEnabled(testUser))
+ underTest.setEnabled(testUser, false)
+ runCurrent()
+
+ assertThat(isCameraSensorPrivacySettingEnabled).hasSize(1)
+ assertThat(isCameraSensorPrivacySettingEnabled.first()).isFalse()
+ }
+
+ @Test
+ fun isCameraSensorPrivacyEnabled_canSetValue3Times() = runTest {
+ val isCameraSensorPrivacySettingEnabled by collectValues(underTest.isEnabled(testUser))
+ runCurrent()
+ underTest.setEnabled(testUser, true)
+ runCurrent()
+ underTest.setEnabled(testUser, false)
+ runCurrent()
+ underTest.setEnabled(testUser, true)
+ runCurrent()
+ assertThat(isCameraSensorPrivacySettingEnabled).hasSize(4)
+ assertThat(isCameraSensorPrivacySettingEnabled.last()).isTrue()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 92396e0bcdef..ce6445b75fb9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -21,9 +21,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dock.DockManager
import com.android.systemui.dock.dockManager
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -40,6 +39,7 @@ import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,8 +79,8 @@ class CommunalSceneStartableTest : SysuiTestCase() {
testScope.runTest {
val scene by collectLastValue(communalInteractor.desiredScene)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.PRIMARY_BOUNCER,
@@ -88,16 +88,17 @@ class CommunalSceneStartableTest : SysuiTestCase() {
testScope = this
)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
}
+ @Ignore("Ignored until custom animations are implemented in b/322787129")
@Test
fun deviceDocked_forceCommunalScene() =
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalInteractor.desiredScene)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
updateDocked(true)
fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -105,7 +106,24 @@ class CommunalSceneStartableTest : SysuiTestCase() {
to = KeyguardState.LOCKSCREEN,
testScope = this
)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ }
+ }
+
+ @Test
+ fun exitingDream_forceCommunalScene() =
+ with(kosmos) {
+ testScope.runTest {
+ val scene by collectLastValue(communalInteractor.desiredScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+
+ updateDocked(true)
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = this
+ )
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
}
@@ -114,7 +132,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalInteractor.desiredScene)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
updateDocked(true)
fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -122,7 +140,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
to = KeyguardState.LOCKSCREEN,
testScope = this
)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
}
@@ -131,19 +149,19 @@ class CommunalSceneStartableTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalInteractor.desiredScene)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.OFF,
testScope = this
)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
}
@@ -152,17 +170,17 @@ class CommunalSceneStartableTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalInteractor.desiredScene)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.OFF,
testScope = this
)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY / 2)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OFF,
@@ -171,15 +189,16 @@ class CommunalSceneStartableTest : SysuiTestCase() {
)
advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
}
+ @Ignore("Ignored until custom animations are implemented in b/322787129")
@Test
fun dockingOnLockscreen_forcesCommunal() =
with(kosmos) {
testScope.runTest {
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ communalInteractor.onSceneChanged(CommunalScenes.Blank)
val scene by collectLastValue(communalInteractor.desiredScene)
// device is docked while on the lockscreen
@@ -190,17 +209,18 @@ class CommunalSceneStartableTest : SysuiTestCase() {
)
updateDocked(true)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
}
+ @Ignore("Ignored until custom animations are implemented in b/322787129")
@Test
fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() =
with(kosmos) {
testScope.runTest {
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ communalInteractor.onSceneChanged(CommunalScenes.Blank)
val scene by collectLastValue(communalInteractor.desiredScene)
// device is docked while on the lockscreen
@@ -211,9 +231,9 @@ class CommunalSceneStartableTest : SysuiTestCase() {
)
updateDocked(true)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY / 2)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
// dream starts shortly after docking
fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -222,7 +242,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
testScope = this
)
advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY)
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
}
@@ -230,7 +250,8 @@ class CommunalSceneStartableTest : SysuiTestCase() {
with(kosmos) {
runCurrent()
fakeDockManager.setIsDocked(docked)
- fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
+ // TODO(b/322787129): uncomment once custom animations are in place
+ // fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 06b3806cb382..43acf3197fb1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -18,9 +18,9 @@ package com.android.systemui.communal.data.repository
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
@@ -60,20 +60,17 @@ class CommunalRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
val transitionState by collectLastValue(underTest.transitionState)
assertThat(transitionState)
- .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
+ .isEqualTo(ObservableTransitionState.Idle(CommunalScenes.Default))
}
@Test
fun transitionState_setTransitionState_returnsNewValue() =
testScope.runTest {
- val expectedSceneKey = CommunalSceneKey.Communal
- underTest.setTransitionState(
- flowOf(ObservableCommunalTransitionState.Idle(expectedSceneKey))
- )
+ val expectedSceneKey = CommunalScenes.Communal
+ underTest.setTransitionState(flowOf(ObservableTransitionState.Idle(expectedSceneKey)))
val transitionState by collectLastValue(underTest.transitionState)
- assertThat(transitionState)
- .isEqualTo(ObservableCommunalTransitionState.Idle(expectedSceneKey))
+ assertThat(transitionState).isEqualTo(ObservableTransitionState.Idle(expectedSceneKey))
}
@Test
@@ -81,7 +78,7 @@ class CommunalRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
// Set a value for the transition state flow.
underTest.setTransitionState(
- flowOf(ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal))
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
)
// Set the transition state flow back to null.
@@ -90,6 +87,6 @@ class CommunalRepositoryImplTest : SysuiTestCase() {
// Flow returns default scene key.
val transitionState by collectLastValue(underTest.transitionState)
assertThat(transitionState)
- .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
+ .isEqualTo(ObservableTransitionState.Idle(CommunalScenes.Default))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 6e3573b64f9a..eafd5038759c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -25,6 +25,7 @@ import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl
@@ -40,9 +41,8 @@ import com.android.systemui.communal.data.repository.fakeCommunalTutorialReposit
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
@@ -53,7 +53,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
@@ -462,9 +462,9 @@ class CommunalInteractorTest : SysuiTestCase() {
var desiredScene = collectLastValue(underTest.desiredScene)
runCurrent()
- assertThat(desiredScene()).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(desiredScene()).isEqualTo(CommunalScenes.Blank)
- val targetScene = CommunalSceneKey.Communal
+ val targetScene = CommunalScenes.Communal
communalRepository.setDesiredScene(targetScene)
desiredScene = collectLastValue(underTest.desiredScene)
runCurrent()
@@ -474,7 +474,7 @@ class CommunalInteractorTest : SysuiTestCase() {
@Test
fun updatesScene() =
testScope.runTest {
- val targetScene = CommunalSceneKey.Communal
+ val targetScene = CommunalScenes.Communal
underTest.onSceneChanged(targetScene)
@@ -491,32 +491,32 @@ class CommunalInteractorTest : SysuiTestCase() {
val desiredScene by collectLastValue(underTest.desiredScene)
- underTest.onSceneChanged(CommunalSceneKey.Communal)
- assertThat(desiredScene).isEqualTo(CommunalSceneKey.Communal)
+ underTest.onSceneChanged(CommunalScenes.Communal)
+ assertThat(desiredScene).isEqualTo(CommunalScenes.Communal)
kosmos.setCommunalAvailable(false)
runCurrent()
// Scene returns blank when communal is not available.
- assertThat(desiredScene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(desiredScene).isEqualTo(CommunalScenes.Blank)
kosmos.setCommunalAvailable(true)
runCurrent()
// After re-enabling, scene goes back to Communal.
- assertThat(desiredScene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(desiredScene).isEqualTo(CommunalScenes.Communal)
}
@Test
fun transitionProgress_onTargetScene_fullProgress() =
testScope.runTest {
- val targetScene = CommunalSceneKey.Blank
+ val targetScene = CommunalScenes.Blank
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
val transitionProgress by collectLastValue(transitionProgressFlow)
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(targetScene)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(targetScene)
)
underTest.setTransitionState(transitionState)
@@ -527,14 +527,14 @@ class CommunalInteractorTest : SysuiTestCase() {
@Test
fun transitionProgress_notOnTargetScene_noProgress() =
testScope.runTest {
- val targetScene = CommunalSceneKey.Blank
- val currentScene = CommunalSceneKey.Communal
+ val targetScene = CommunalScenes.Blank
+ val currentScene = CommunalScenes.Communal
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
val transitionProgress by collectLastValue(transitionProgressFlow)
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(currentScene)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(currentScene)
)
underTest.setTransitionState(transitionState)
@@ -545,14 +545,14 @@ class CommunalInteractorTest : SysuiTestCase() {
@Test
fun transitionProgress_transitioningToTrackedScene() =
testScope.runTest {
- val currentScene = CommunalSceneKey.Communal
- val targetScene = CommunalSceneKey.Blank
+ val currentScene = CommunalScenes.Communal
+ val targetScene = CommunalScenes.Blank
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
val transitionProgress by collectLastValue(transitionProgressFlow)
var transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(currentScene)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(currentScene)
)
underTest.setTransitionState(transitionState)
@@ -562,7 +562,7 @@ class CommunalInteractorTest : SysuiTestCase() {
val progress = MutableStateFlow(0f)
transitionState =
MutableStateFlow(
- ObservableCommunalTransitionState.Transition(
+ ObservableTransitionState.Transition(
fromScene = currentScene,
toScene = targetScene,
progress = progress,
@@ -581,7 +581,7 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Transition(1f))
// Transition finishes.
- transitionState = MutableStateFlow(ObservableCommunalTransitionState.Idle(targetScene))
+ transitionState = MutableStateFlow(ObservableTransitionState.Idle(targetScene))
underTest.setTransitionState(transitionState)
assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
}
@@ -589,14 +589,14 @@ class CommunalInteractorTest : SysuiTestCase() {
@Test
fun transitionProgress_transitioningAwayFromTrackedScene() =
testScope.runTest {
- val currentScene = CommunalSceneKey.Blank
- val targetScene = CommunalSceneKey.Communal
+ val currentScene = CommunalScenes.Blank
+ val targetScene = CommunalScenes.Communal
val transitionProgressFlow = underTest.transitionProgressToScene(currentScene)
val transitionProgress by collectLastValue(transitionProgressFlow)
var transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(currentScene)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(currentScene)
)
underTest.setTransitionState(transitionState)
@@ -606,7 +606,7 @@ class CommunalInteractorTest : SysuiTestCase() {
val progress = MutableStateFlow(0f)
transitionState =
MutableStateFlow(
- ObservableCommunalTransitionState.Transition(
+ ObservableTransitionState.Transition(
fromScene = currentScene,
toScene = targetScene,
progress = progress,
@@ -627,7 +627,7 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.OtherTransition)
// Transition finishes.
- transitionState = MutableStateFlow(ObservableCommunalTransitionState.Idle(targetScene))
+ transitionState = MutableStateFlow(ObservableTransitionState.Idle(targetScene))
underTest.setTransitionState(transitionState)
assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
}
@@ -642,7 +642,7 @@ class CommunalInteractorTest : SysuiTestCase() {
runCurrent()
assertThat(isCommunalShowing()).isEqualTo(false)
- underTest.onSceneChanged(CommunalSceneKey.Communal)
+ underTest.onSceneChanged(CommunalScenes.Communal)
isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
runCurrent()
@@ -661,17 +661,17 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(isCommunalShowing).isFalse()
// Verify scene changes with the flag doesn't have any impact
- sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "")
+ sceneInteractor.changeScene(Scenes.Communal, loggingReason = "")
runCurrent()
assertThat(isCommunalShowing).isFalse()
// Verify scene changes (without the flag) to communal sets the value to true
- underTest.onSceneChanged(CommunalSceneKey.Communal)
+ underTest.onSceneChanged(CommunalScenes.Communal)
runCurrent()
assertThat(isCommunalShowing).isTrue()
// Verify scene changes (without the flag) to blank sets the value back to false
- underTest.onSceneChanged(CommunalSceneKey.Blank)
+ underTest.onSceneChanged(CommunalScenes.Blank)
runCurrent()
assertThat(isCommunalShowing).isFalse()
}
@@ -687,17 +687,17 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(isCommunalShowing).isFalse()
// Verify scene changes without the flag doesn't have any impact
- underTest.onSceneChanged(CommunalSceneKey.Communal)
+ underTest.onSceneChanged(CommunalScenes.Communal)
runCurrent()
assertThat(isCommunalShowing).isFalse()
// Verify scene changes (with the flag) to communal sets the value to true
- sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "")
+ sceneInteractor.changeScene(Scenes.Communal, loggingReason = "")
runCurrent()
assertThat(isCommunalShowing).isTrue()
// Verify scene changes (with the flag) to lockscreen sets the value to false
- sceneInteractor.changeScene(SceneKey.Lockscreen, loggingReason = "")
+ sceneInteractor.changeScene(Scenes.Lockscreen, loggingReason = "")
runCurrent()
assertThat(isCommunalShowing).isFalse()
}
@@ -706,8 +706,8 @@ class CommunalInteractorTest : SysuiTestCase() {
fun isIdleOnCommunal() =
testScope.runTest {
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Blank)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Blank)
)
communalRepository.setTransitionState(transitionState)
@@ -717,8 +717,7 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(isIdleOnCommunal).isEqualTo(false)
// Transition to communal.
- transitionState.value =
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
runCurrent()
// isIdleOnCommunal is now true since we're on communal.
@@ -726,9 +725,9 @@ class CommunalInteractorTest : SysuiTestCase() {
// Start transition away from communal.
transitionState.value =
- ObservableCommunalTransitionState.Transition(
- fromScene = CommunalSceneKey.Communal,
- toScene = CommunalSceneKey.Blank,
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
progress = flowOf(0f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -743,8 +742,8 @@ class CommunalInteractorTest : SysuiTestCase() {
fun isCommunalVisible() =
testScope.runTest {
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Blank)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Blank)
)
communalRepository.setTransitionState(transitionState)
@@ -754,9 +753,9 @@ class CommunalInteractorTest : SysuiTestCase() {
// Start transition to communal.
transitionState.value =
- ObservableCommunalTransitionState.Transition(
- fromScene = CommunalSceneKey.Blank,
- toScene = CommunalSceneKey.Communal,
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Blank,
+ toScene = CommunalScenes.Communal,
progress = flowOf(0f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -766,17 +765,16 @@ class CommunalInteractorTest : SysuiTestCase() {
assertThat(isCommunalVisible).isEqualTo(true)
// Finish transition to communal
- transitionState.value =
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
// isCommunalVisible is true since we're on communal.
assertThat(isCommunalVisible).isEqualTo(true)
// Start transition away from communal.
transitionState.value =
- ObservableCommunalTransitionState.Transition(
- fromScene = CommunalSceneKey.Communal,
- toScene = CommunalSceneKey.Blank,
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
progress = flowOf(1.0f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 8b785927ba5e..50b8da62b3f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -25,7 +25,7 @@ import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -158,7 +158,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
kosmos.setCommunalAvailable(true)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ communalInteractor.onSceneChanged(CommunalScenes.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
}
@@ -171,7 +171,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ communalInteractor.onSceneChanged(CommunalScenes.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
@@ -184,13 +184,13 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ communalInteractor.onSceneChanged(CommunalScenes.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
private suspend fun goToCommunal() {
kosmos.setCommunalAvailable(true)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
index 6b1b93777fbc..a51315bd96b8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
@@ -18,13 +18,14 @@ package com.android.systemui.communal.log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -73,7 +74,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
testScope.runTest {
// Transition state is default (non-communal)
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT))
+ MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default))
communalInteractor.setTransitionState(transitionState)
runCurrent()
@@ -81,14 +82,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
verify(uiEventLogger, never()).log(any())
// Start transition to communal
- transitionState.value = transition(to = CommunalSceneKey.Communal)
+ transitionState.value = transition(to = CommunalScenes.Communal)
runCurrent()
// Verify UiEvent logged
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START)
// Finish transition to communal
- transitionState.value = idle(CommunalSceneKey.Communal)
+ transitionState.value = idle(CommunalScenes.Communal)
runCurrent()
// Verify UiEvent logged
@@ -101,7 +102,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
testScope.runTest {
// Transition state is default (non-communal)
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT))
+ MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default))
communalInteractor.setTransitionState(transitionState)
runCurrent()
@@ -109,14 +110,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
verify(uiEventLogger, never()).log(any())
// Start transition to communal
- transitionState.value = transition(to = CommunalSceneKey.Communal)
+ transitionState.value = transition(to = CommunalScenes.Communal)
runCurrent()
// Verify UiEvent logged
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START)
// Cancel the transition
- transitionState.value = idle(CommunalSceneKey.DEFAULT)
+ transitionState.value = idle(CommunalScenes.Default)
runCurrent()
// Verify UiEvent logged
@@ -132,7 +133,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
testScope.runTest {
// Transition state is communal
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal))
+ MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal))
communalInteractor.setTransitionState(transitionState)
runCurrent()
@@ -140,14 +141,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
// Start transition from communal
- transitionState.value = transition(from = CommunalSceneKey.Communal)
+ transitionState.value = transition(from = CommunalScenes.Communal)
runCurrent()
// Verify UiEvent logged
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START)
// Finish transition to communal
- transitionState.value = idle(CommunalSceneKey.DEFAULT)
+ transitionState.value = idle(CommunalScenes.Default)
runCurrent()
// Verify UiEvent logged
@@ -160,7 +161,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
testScope.runTest {
// Transition state is communal
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal))
+ MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal))
communalInteractor.setTransitionState(transitionState)
runCurrent()
@@ -168,14 +169,14 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
clearInvocations(uiEventLogger)
// Start transition from communal
- transitionState.value = transition(from = CommunalSceneKey.Communal)
+ transitionState.value = transition(from = CommunalScenes.Communal)
runCurrent()
// Verify UiEvent logged
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START)
// Cancel the transition
- transitionState.value = idle(CommunalSceneKey.Communal)
+ transitionState.value = idle(CommunalScenes.Communal)
runCurrent()
// Verify UiEvent logged
@@ -187,10 +188,10 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
}
private fun transition(
- from: CommunalSceneKey = CommunalSceneKey.DEFAULT,
- to: CommunalSceneKey = CommunalSceneKey.DEFAULT,
- ): ObservableCommunalTransitionState.Transition {
- return ObservableCommunalTransitionState.Transition(
+ from: SceneKey = CommunalScenes.Default,
+ to: SceneKey = CommunalScenes.Default,
+ ): ObservableTransitionState.Transition {
+ return ObservableTransitionState.Transition(
fromScene = from,
toScene = to,
progress = emptyFlow(),
@@ -199,7 +200,7 @@ class CommunalLoggerStartableTest : SysuiTestCase() {
)
}
- private fun idle(sceneKey: CommunalSceneKey): ObservableCommunalTransitionState.Idle {
- return ObservableCommunalTransitionState.Idle(sceneKey)
+ private fun idle(sceneKey: SceneKey): ObservableTransitionState.Idle {
+ return ObservableTransitionState.Idle(sceneKey)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index b611e0aafa2f..36919d0c74a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1052,32 +1052,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
faceAuthenticateIsCalled()
}
- @Test
- fun authFailedCallAfterAuthLockedOutErrorShouldBeIgnored() =
- testScope.runTest {
- initCollectors()
- allPreconditionsToRunFaceAuthAreTrue()
- runCurrent()
- assertThat(canFaceAuthRun()).isTrue()
-
- underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
- runCurrent()
-
- faceAuthenticateIsCalled()
- authenticationCallback.value.onAuthenticationError(
- FACE_ERROR_LOCKOUT_PERMANENT,
- "Too many attempts, face not available"
- )
-
- val lockoutError = authStatus() as ErrorFaceAuthenticationStatus
- assertThat(lockedOut()).isTrue()
- assertThat(lockoutError.isLockoutError()).isTrue()
-
- authenticationCallback.value.onAuthenticationFailed()
- runCurrent()
-
- assertThat(authStatus()).isEqualTo(lockoutError)
- }
private suspend fun TestScope.testGatingCheckForFaceAuth(
gatingCheckModifier: suspend () -> Unit
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 98719dd32e5a..4f44705b7e72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.deviceentry.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -31,7 +32,7 @@ import com.android.systemui.keyguard.data.repository.fakeTrustRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -120,7 +121,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
setupSwipeDeviceEntryMethod()
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
assertThat(isDeviceEntered).isFalse()
}
@@ -130,9 +131,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
setupSwipeDeviceEntryMethod()
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
runCurrent()
- switchToScene(SceneKey.Shade)
+ switchToScene(Scenes.Shade)
assertThat(isDeviceEntered).isFalse()
}
@@ -142,9 +143,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
setupSwipeDeviceEntryMethod()
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
runCurrent()
- switchToScene(SceneKey.Gone)
+ switchToScene(Scenes.Gone)
assertThat(isDeviceEntered).isTrue()
}
@@ -154,11 +155,11 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
setupSwipeDeviceEntryMethod()
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
runCurrent()
- switchToScene(SceneKey.Gone)
+ switchToScene(Scenes.Gone)
runCurrent()
- switchToScene(SceneKey.Shade)
+ switchToScene(Scenes.Shade)
assertThat(isDeviceEntered).isTrue()
}
@@ -170,9 +171,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
AuthenticationMethodModel.Pattern
)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
runCurrent()
- switchToScene(SceneKey.Bouncer)
+ switchToScene(Scenes.Bouncer)
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
assertThat(isDeviceEntered).isFalse()
@@ -182,7 +183,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
fun canSwipeToEnter_onLockscreenWithSwipe_isTrue() =
testScope.runTest {
setupSwipeDeviceEntryMethod()
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
assertThat(canSwipeToEnter).isTrue()
@@ -195,7 +196,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
AuthenticationMethodModel.Pin
)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
assertThat(canSwipeToEnter).isFalse()
@@ -205,9 +206,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
fun canSwipeToEnter_afterLockscreenDismissedInSwipeMode_isFalse() =
testScope.runTest {
setupSwipeDeviceEntryMethod()
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
runCurrent()
- switchToScene(SceneKey.Gone)
+ switchToScene(Scenes.Gone)
val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
assertThat(canSwipeToEnter).isFalse()
@@ -225,7 +226,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password
)
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
assertThat(canSwipeToEnter).isFalse()
trustRepository.setCurrentUserTrusted(true)
@@ -242,7 +243,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password
)
- switchToScene(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
assertThat(canSwipeToEnter).isFalse()
faceAuthRepository.isAuthenticated.value = true
@@ -311,8 +312,8 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
- switchToScene(SceneKey.Lockscreen)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin
@@ -322,15 +323,15 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
underTest.attemptDeviceEntry()
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
}
@Test
fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
- switchToScene(SceneKey.Lockscreen)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.None
@@ -339,15 +340,15 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
underTest.attemptDeviceEntry()
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
}
@Test
fun showOrUnlockDevice_authMethodSwipe_switchesToGoneScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
- switchToScene(SceneKey.Lockscreen)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ switchToScene(Scenes.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
@@ -357,7 +358,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
underTest.attemptDeviceEntry()
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index fb46ed9d54ed..b3380ff6409c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -25,18 +25,17 @@ import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
-import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -49,9 +48,10 @@ import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class LightRevealScrimRepositoryTest : SysuiTestCase() {
- private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
- private lateinit var powerRepository: FakePowerRepository
- private lateinit var powerInteractor: PowerInteractor
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val fakeKeyguardRepository = kosmos.fakeKeyguardRepository
+ private val powerInteractor = kosmos.powerInteractor
private lateinit var underTest: LightRevealScrimRepositoryImpl
@get:Rule val animatorTestRule = AnimatorTestRule(this)
@@ -59,13 +59,13 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- fakeKeyguardRepository = FakeKeyguardRepository()
- powerRepository = FakePowerRepository()
- powerInteractor =
- PowerInteractorFactory.create(repository = powerRepository).powerInteractor
-
underTest =
- LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context, powerInteractor, mock())
+ LightRevealScrimRepositoryImpl(
+ kosmos.fakeKeyguardRepository,
+ context,
+ kosmos.powerInteractor,
+ mock()
+ )
}
@Test
@@ -168,8 +168,9 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
fun revealAmount_emitsTo1AfterAnimationStarted() =
- runTest(UnconfinedTestDispatcher()) {
+ testScope.runTest {
val value by collectLastValue(underTest.revealAmount)
+ runCurrent()
underTest.startRevealAmountAnimator(true)
assertEquals(0.0f, value)
animatorTestRule.advanceTimeBy(500L)
@@ -179,8 +180,9 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
fun revealAmount_startingRevealTwiceWontRerunAnimator() =
- runTest(UnconfinedTestDispatcher()) {
+ testScope.runTest {
val value by collectLastValue(underTest.revealAmount)
+ runCurrent()
underTest.startRevealAmountAnimator(true)
assertEquals(0.0f, value)
animatorTestRule.advanceTimeBy(250L)
@@ -193,12 +195,14 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
fun revealAmount_emitsTo0AfterAnimationStartedReversed() =
- runTest(UnconfinedTestDispatcher()) {
- val value by collectLastValue(underTest.revealAmount)
+ testScope.runTest {
+ val lastValue by collectLastValue(underTest.revealAmount)
+ runCurrent()
+ underTest.startRevealAmountAnimator(true)
+ animatorTestRule.advanceTimeBy(500L)
underTest.startRevealAmountAnimator(false)
- assertEquals(1.0f, value)
animatorTestRule.advanceTimeBy(500L)
- assertEquals(0.0f, value)
+ assertEquals(0.0f, lastValue)
}
/**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index ef2b6f0805d6..24c651f3c702 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -18,8 +18,11 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
+import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
@@ -35,8 +38,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -64,7 +66,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
private val shadeRepository = FakeShadeRepository()
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
private val transitionState: MutableStateFlow<ObservableTransitionState> =
- MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Gone))
+ MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone))
private val underTest by lazy {
KeyguardInteractor(
@@ -120,6 +122,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testKeyguardGuardVisibilityStopsSecureCamera() =
testScope.runTest {
val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
@@ -144,6 +147,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testBouncerShowingResetsSecureCameraState() =
testScope.runTest {
val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
@@ -166,6 +170,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
val isVisible = collectLastValue(underTest.isKeyguardVisible)
repository.setKeyguardShowing(true)
@@ -250,8 +255,8 @@ class KeyguardInteractorTest : SysuiTestCase() {
underTest.setAnimateDozingTransitions(true)
transitionState.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Gone,
- toScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Gone,
+ toScene = Scenes.Lockscreen,
progress = flowOf(0f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 63abc8f34668..0ebcf5608bff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
@@ -49,6 +50,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
@@ -57,6 +59,7 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -80,6 +83,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogTransitionAnimator
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -179,6 +183,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = withDeps.keyguardInteractor,
+ shadeInteractor = shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
@@ -193,6 +198,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
backgroundDispatcher = testDispatcher,
appContext = context,
)
+
+ whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
}
@Test
@@ -339,6 +346,25 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
}
@Test
+ fun quickAffordance_updateOncePerShadeExpansion() =
+ testScope.runTest {
+ val shadeExpansion = MutableStateFlow(0f)
+ whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion)
+
+ val collectedValue by
+ collectValues(
+ underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
+ )
+
+ val initialSize = collectedValue.size
+ for (i in 0..10) {
+ shadeExpansion.value = i / 10f
+ }
+
+ assertThat(collectedValue.size).isEqualTo(initialSize + 1)
+ }
+
+ @Test
fun quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManager() =
testScope.runTest {
whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
index 4c972e9195e9..a3371d3d24f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
@@ -42,6 +42,25 @@ class AodToGoneTransitionViewModelTest : SysuiTestCase() {
val underTest = kosmos.aodToGoneTransitionViewModel
@Test
+ fun lockscreenAlpha() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor(alpha = { 0.5f })
+ val alpha by collectValues(underTest.lockscreenAlpha(viewState))
+
+ repository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ assertThat(alpha[0]).isEqualTo(0.5f)
+ // Fades out just prior to halfway
+ assertThat(alpha[1]).isEqualTo(0f)
+ // Must finish at 0
+ assertThat(alpha[2]).isEqualTo(0f)
+ }
+
+ @Test
fun deviceEntryParentViewHides() =
testScope.runTest {
val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
index 7e937db842ff..79671b885c56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
@@ -51,6 +51,25 @@ class DozingToGoneTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ fun lockscreenAlpha() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor(alpha = { 0.6f })
+ val alpha by collectValues(underTest.lockscreenAlpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ assertThat(alpha[0]).isEqualTo(0.6f)
+ // Fades out just prior to halfway
+ assertThat(alpha[1]).isEqualTo(0f)
+ // Must finish at 0
+ assertThat(alpha[2]).isEqualTo(0f)
+ }
+
+ @Test
fun deviceEntryParentViewDisappear() =
testScope.runTest {
val values by collectValues(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 503fd34ce2c2..979d50463a04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -22,12 +22,12 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalRepository
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.flags.Flags
@@ -262,7 +262,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
// Hub transition state is idle with hub open.
communalRepository.setTransitionState(
- flowOf(ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal))
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
)
runCurrent()
@@ -328,4 +328,42 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
shadeRepository.setQsExpansion(0.5f)
assertThat(alpha).isEqualTo(0f)
}
+
+ @Test
+ fun alpha_idleOnOccluded_isZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+ assertThat(alpha).isEqualTo(1f)
+
+ // Go to OCCLUDED state
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope = testScope,
+ )
+ assertThat(alpha).isEqualTo(0f)
+
+ // Try pulling down shade and ensure the value doesn't change
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun alpha_idleOnGone_isZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+ assertThat(alpha).isEqualTo(1f)
+
+ // Go to GONE state
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+ assertThat(alpha).isEqualTo(0f)
+
+ // Try pulling down shade and ensure the value doesn't change
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index d4dd2ac78e2a..ad1cef1c1e92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -46,6 +47,7 @@ class LockscreenContentViewModelTest : SysuiTestCase() {
fun setup() {
with(kosmos) {
fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
underTest = lockscreenContentViewModel
}
}
@@ -87,11 +89,21 @@ class LockscreenContentViewModelTest : SysuiTestCase() {
}
@Test
+ fun areNotificationsVisible_splitShadeTrue_true() =
+ with(kosmos) {
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+
+ assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+ }
+ }
+ @Test
fun areNotificationsVisible_withSmallClock_true() =
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
- assertThat(underTest.areNotificationsVisible).isTrue()
+ assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
}
}
@@ -100,7 +112,7 @@ class LockscreenContentViewModelTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- assertThat(underTest.areNotificationsVisible).isFalse()
+ assertThat(underTest.areNotificationsVisible(context.resources)).isFalse()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 3104842a9c2a..9ff76be30f79 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -32,7 +32,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
@@ -62,9 +62,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
- sceneInteractor.changeScene(SceneKey.Lockscreen, "reason")
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(upTransitionSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -75,9 +75,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.Pin
)
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneKey.Lockscreen, "reason")
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(upTransitionSceneKey).isEqualTo(Scenes.Bouncer)
}
@EnableFlags(FLAG_COMMUNAL_HUB)
@@ -89,7 +89,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
kosmos.setCommunalAvailable(true)
runCurrent()
- assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
+ assertThat(leftDestinationSceneKey).isEqualTo(Scenes.Communal)
}
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
new file mode 100644
index 000000000000..266875e9e12a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.rotation.domain.interactor
+
+import android.Manifest
+import android.content.packageManager
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.platform.test.annotations.EnabledOnRavenwood
+import android.testing.LeakCheck
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.camera.data.repository.fakeCameraAutoRotateRepository
+import com.android.systemui.camera.data.repository.fakeCameraSensorPrivacyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.leaks.FakeBatteryController
+import com.android.systemui.utils.leaks.FakeRotationLockController
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toCollection
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class RotationLockTileDataInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val batteryController = FakeBatteryController(LeakCheck())
+ private val rotationController = FakeRotationLockController(LeakCheck())
+ private val fakeCameraAutoRotateRepository = kosmos.fakeCameraAutoRotateRepository
+ private val fakeCameraSensorPrivacyRepository = kosmos.fakeCameraSensorPrivacyRepository
+ private val packageManager = kosmos.packageManager
+
+ private val testUser = UserHandle.of(1)
+ private lateinit var underTest: RotationLockTileDataInteractor
+
+ @Before
+ fun setup() {
+ whenever(packageManager.rotationResolverPackageName).thenReturn(TEST_PACKAGE_NAME)
+ whenever(
+ packageManager.checkPermission(
+ eq(Manifest.permission.CAMERA),
+ eq(TEST_PACKAGE_NAME)
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+
+ underTest =
+ RotationLockTileDataInteractor(
+ rotationController,
+ batteryController,
+ fakeCameraAutoRotateRepository,
+ fakeCameraSensorPrivacyRepository,
+ packageManager,
+ context.orCreateTestableResources
+ .apply {
+ addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
+ }
+ .resources
+ )
+ }
+
+ @Test
+ fun availability_isTrue() =
+ testScope.runTest {
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).hasSize(1)
+ assertThat(availability.last()).isTrue()
+ }
+
+ @Test
+ fun tileData_isRotationLockedMatchesRotationController() =
+ testScope.runTest {
+ val data by
+ collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+
+ runCurrent()
+ assertThat(data!!.isRotationLocked).isEqualTo(false)
+
+ rotationController.setRotationLocked(true, CALLER)
+ runCurrent()
+ assertThat(data!!.isRotationLocked).isEqualTo(true)
+
+ rotationController.setRotationLocked(false, CALLER)
+ runCurrent()
+ assertThat(data!!.isRotationLocked).isEqualTo(false)
+ }
+
+ @Test
+ fun tileData_cameraRotationMatchesBatteryController() =
+ testScope.runTest {
+ setupControllersToEnableCameraRotation()
+ val data by
+ collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+
+ runCurrent()
+ assertThat(data!!.isCameraRotationEnabled).isTrue()
+
+ batteryController.setPowerSaveMode(true)
+ runCurrent()
+ assertThat(data!!.isCameraRotationEnabled).isFalse()
+
+ batteryController.setPowerSaveMode(false)
+ runCurrent()
+ assertThat(data!!.isCameraRotationEnabled).isTrue()
+ }
+
+ @Test
+ fun tileData_cameraRotationMatchesSensorPrivacyRepository() =
+ testScope.runTest {
+ setupControllersToEnableCameraRotation()
+ val lastValue by
+ this.collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ assertThat(lastValue!!.isCameraRotationEnabled).isTrue()
+
+ fakeCameraSensorPrivacyRepository.setEnabled(testUser, true)
+ runCurrent()
+ assertThat(lastValue!!.isCameraRotationEnabled).isFalse()
+
+ fakeCameraSensorPrivacyRepository.setEnabled(testUser, false)
+ runCurrent()
+ assertThat(lastValue!!.isCameraRotationEnabled).isTrue()
+ }
+
+ @Test
+ fun tileData_cameraRotationMatchesAutoRotateRepository() =
+ testScope.runTest {
+ setupControllersToEnableCameraRotation()
+
+ val lastValue by
+ collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ assertThat(lastValue!!.isCameraRotationEnabled).isTrue()
+
+ fakeCameraAutoRotateRepository.setEnabled(testUser, false)
+ runCurrent()
+ assertThat(lastValue!!.isCameraRotationEnabled).isFalse()
+
+ fakeCameraAutoRotateRepository.setEnabled(testUser, true)
+ runCurrent()
+ assertThat(lastValue!!.isCameraRotationEnabled).isTrue()
+ }
+
+ @Test
+ fun tileData_matchesPackageManagerPermissionDenied() =
+ testScope.runTest {
+ whenever(
+ packageManager.checkPermission(
+ eq(Manifest.permission.CAMERA),
+ eq(TEST_PACKAGE_NAME)
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_DENIED)
+
+ val lastValue by
+ collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ assertThat(lastValue!!.isCameraRotationEnabled).isEqualTo(false)
+ }
+
+ @Test
+ fun tileData_setConfigAllowRotationResolverToFalse_cameraRotationIsNotEnabled() =
+ testScope.runTest {
+ underTest.apply {
+ overrideResource(com.android.internal.R.bool.config_allowRotationResolver, false)
+ }
+ setupControllersToEnableCameraRotation()
+ val lastValue by
+ collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+
+ assertThat(lastValue!!.isCameraRotationEnabled).isEqualTo(false)
+ }
+
+ private fun setupControllersToEnableCameraRotation() {
+ rotationController.setRotationLocked(true, CALLER)
+ batteryController.setPowerSaveMode(false)
+ fakeCameraSensorPrivacyRepository.setEnabled(testUser, false)
+ fakeCameraAutoRotateRepository.setEnabled(testUser, true)
+ }
+
+ private companion object {
+ private const val CALLER = "RotationLockTileDataInteractorTest"
+ private const val TEST_PACKAGE_NAME = "com.test"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
new file mode 100644
index 000000000000..1653ce369ea1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.rotation.domain.interactor
+
+import android.platform.test.annotations.EnabledOnRavenwood
+import android.provider.Settings
+import android.testing.LeakCheck
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
+import com.android.systemui.utils.leaks.FakeRotationLockController
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class RotationLockTileUserActionInteractorTest : SysuiTestCase() {
+ private val controller = FakeRotationLockController(LeakCheck())
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+
+ private val underTest =
+ RotationLockTileUserActionInteractor(
+ controller,
+ inputHandler,
+ )
+
+ @Test
+ fun handleClickWhenEnabled() = runTest {
+ val wasEnabled = true
+ controller.setRotationLocked(wasEnabled, null)
+
+ underTest.handleInput(QSTileInputTestKtx.click(RotationLockTileModel(wasEnabled, false)))
+
+ assertThat(controller.isRotationLocked).isEqualTo(!wasEnabled)
+ }
+
+ @Test
+ fun handleClickWhenDisabled() = runTest {
+ val wasEnabled = false
+ controller.setRotationLocked(wasEnabled, null)
+
+ underTest.handleInput(QSTileInputTestKtx.click(RotationLockTileModel(wasEnabled, false)))
+
+ assertThat(controller.isRotationLocked).isEqualTo(!wasEnabled)
+ }
+
+ @Test
+ fun handleLongClickWhenDisabled() = runTest {
+ val enabled = false
+
+ underTest.handleInput(
+ QSTileInputTestKtx.longClick(
+ RotationLockTileModel(
+ enabled,
+ false,
+ )
+ )
+ )
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTO_ROTATE_SETTINGS)
+ }
+ }
+
+ @Test
+ fun handleLongClickWhenEnabled() = runTest {
+ val enabled = true
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(RotationLockTileModel(enabled, false)))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTO_ROTATE_SETTINGS)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
new file mode 100644
index 000000000000..60c69f427ef3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.rotation.ui.mapper
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
+import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.devicePostureController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RotationLockTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig
+ private val devicePostureController = kosmos.devicePostureController
+
+ private lateinit var mapper: RotationLockTileMapper
+
+ @Before
+ fun setup() {
+ whenever(devicePostureController.devicePosture)
+ .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED)
+
+ mapper =
+ RotationLockTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_auto_rotate_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_auto_rotate_icon_on, TestStubDrawable())
+ addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
+ addOverride(
+ com.android.internal.R.array.config_foldedDeviceStates,
+ intArrayOf() // empty array <=> device is not foldable
+ )
+ }
+ .resources,
+ context.theme,
+ devicePostureController
+ )
+ }
+
+ @Test
+ fun rotationNotLocked_cameraRotationDisabled() {
+ val inputModel = RotationLockTileModel(false, false)
+
+ val outputState = mapper.map(rotationLockTileConfig, inputModel)
+
+ val expectedState =
+ createRotationLockTileState(
+ QSTileState.ActivationState.ACTIVE,
+ EMPTY_SECONDARY_STRING,
+ R.drawable.qs_auto_rotate_icon_on
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun rotationNotLocked_cameraRotationEnabled() {
+ val inputModel = RotationLockTileModel(false, true)
+
+ val outputState = mapper.map(rotationLockTileConfig, inputModel)
+
+ val expectedState =
+ createRotationLockTileState(
+ QSTileState.ActivationState.ACTIVE,
+ context.getString(R.string.rotation_lock_camera_rotation_on),
+ R.drawable.qs_auto_rotate_icon_on
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun rotationLocked_cameraRotationNotEnabled() {
+ val inputModel = RotationLockTileModel(true, false)
+
+ val outputState = mapper.map(rotationLockTileConfig, inputModel)
+
+ val expectedState =
+ createRotationLockTileState(
+ QSTileState.ActivationState.INACTIVE,
+ EMPTY_SECONDARY_STRING,
+ R.drawable.qs_auto_rotate_icon_off
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun deviceFoldableAndClosed_secondaryLabelIsFoldableSpecific() {
+ setDeviceFoldable()
+ val inputModel = RotationLockTileModel(false, false)
+
+ val outputState = mapper.map(rotationLockTileConfig, inputModel)
+
+ val expectedSecondaryLabelEnding =
+ context.getString(R.string.quick_settings_rotation_posture_folded)
+ assertThat(
+ context.resources.getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates
+ )
+ )
+ .isNotEmpty()
+ val actualSecondaryLabel = outputState.secondaryLabel
+ assertThat(actualSecondaryLabel).isNotNull()
+ assertThat(actualSecondaryLabel!!.endsWith(expectedSecondaryLabelEnding)).isTrue()
+ }
+
+ @Test
+ fun deviceFoldableAndNotClosed_secondaryLabelIsFoldableSpecific() {
+ setDeviceFoldable()
+ whenever(devicePostureController.devicePosture)
+ .thenReturn(DevicePostureController.DEVICE_POSTURE_OPENED)
+ val inputModel = RotationLockTileModel(false, false)
+
+ val outputState = mapper.map(rotationLockTileConfig, inputModel)
+
+ val expectedSecondaryLabelEnding =
+ context.getString(R.string.quick_settings_rotation_posture_unfolded)
+ assertThat(
+ context.orCreateTestableResources.resources.getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates
+ )
+ )
+ .isNotEmpty()
+ val actualSecondaryLabel = outputState.secondaryLabel
+ assertThat(actualSecondaryLabel).isNotNull()
+ assertThat(actualSecondaryLabel!!.endsWith(expectedSecondaryLabelEnding)).isTrue()
+ }
+
+ private fun setDeviceFoldable() {
+ mapper.apply {
+ overrideResource(
+ com.android.internal.R.array.config_foldedDeviceStates,
+ intArrayOf(1, 2, 3)
+ )
+ }
+ }
+
+ private fun createRotationLockTileState(
+ activationState: QSTileState.ActivationState,
+ secondaryLabel: String,
+ iconRes: Int
+ ): QSTileState {
+ val label = context.getString(R.string.quick_settings_rotation_unlocked_label)
+ return QSTileState(
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ label,
+ activationState,
+ secondaryLabel,
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ context.getString(R.string.accessibility_quick_settings_rotation),
+ secondaryLabel,
+ QSTileState.SideViewIcon.None,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName
+ )
+ }
+
+ private companion object {
+ private const val EMPTY_SECONDARY_STRING = ""
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 1eb9adb8c004..63f00c1356e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -18,6 +18,10 @@ package com.android.systemui.qs.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
@@ -26,10 +30,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@@ -121,8 +122,8 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
assertThat(destinations)
.isEqualTo(
mapOf(
- UserAction.Back to UserActionResult(SceneKey.Shade),
- UserAction.Swipe(Direction.UP) to UserActionResult(SceneKey.Shade),
+ Back to UserActionResult(Scenes.Shade),
+ Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
)
)
}
@@ -136,7 +137,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
assertThat(destinations)
.isEqualTo(
mapOf(
- UserAction.Back to UserActionResult(SceneKey.QuickSettings),
+ Back to UserActionResult(Scenes.QuickSettings),
)
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 667f516317be..a2c4f4e63c19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -22,6 +22,8 @@ import android.telecom.TelecomManager
import android.telephony.TelephonyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.internal.R
import com.android.internal.util.EmergencyAffordanceManager
import com.android.internal.util.emergencyAffordanceManager
@@ -61,8 +63,7 @@ import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.FakeDisplayTracker
@@ -287,19 +288,19 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
}
@Test
- fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(SceneKey.Lockscreen) }
+ fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(Scenes.Lockscreen) }
@Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
- emulateUserDrivenTransition(SceneKey.Bouncer)
+ emulateUserDrivenTransition(Scenes.Bouncer)
fakeSceneDataSource.pause()
enterPin()
emulatePendingTransitionProgress(
expectedVisible = false,
)
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
}
@Test
@@ -307,7 +308,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
testScope.runTest {
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(
to = upDestinationSceneKey,
)
@@ -317,7 +318,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
emulatePendingTransitionProgress(
expectedVisible = false,
)
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
}
@Test
@@ -327,7 +328,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
emulateUserDrivenTransition(
to = upDestinationSceneKey,
)
@@ -338,13 +339,13 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
// Emulate a user swipe to the shade scene.
- emulateUserDrivenTransition(to = SceneKey.Shade)
- assertCurrentScene(SceneKey.Shade)
+ emulateUserDrivenTransition(to = Scenes.Shade)
+ assertCurrentScene(Scenes.Shade)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Lockscreen)
emulateUserDrivenTransition(
to = upDestinationSceneKey,
)
@@ -356,17 +357,17 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
assertThat(deviceEntryInteractor.canSwipeToEnter.value).isTrue()
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
- emulateUserDrivenTransition(to = SceneKey.Gone)
- assertCurrentScene(SceneKey.Gone)
+ emulateUserDrivenTransition(to = Scenes.Gone)
+ assertCurrentScene(Scenes.Gone)
// Emulate a user swipe to the shade scene.
- emulateUserDrivenTransition(to = SceneKey.Shade)
- assertCurrentScene(SceneKey.Shade)
+ emulateUserDrivenTransition(to = Scenes.Shade)
+ assertCurrentScene(Scenes.Shade)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
emulateUserDrivenTransition(
to = upDestinationSceneKey,
)
@@ -377,10 +378,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
testScope.runTest {
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
putDeviceToSleep(instantlyLockDevice = false)
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
wakeUpDevice()
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
}
@Test
@@ -388,45 +389,45 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
testScope.runTest {
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
putDeviceToSleep(instantlyLockDevice = false)
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
wakeUpDevice()
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun deviceGoesToSleep_switchesToLockscreen() =
testScope.runTest {
unlockDevice()
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
putDeviceToSleep()
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun deviceGoesToSleep_wakeUp_unlock() =
testScope.runTest {
unlockDevice()
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
putDeviceToSleep()
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
wakeUpDevice()
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
unlockDevice()
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
}
@Test
fun deviceWakesUpWhileUnlocked_dismissesLockscreen() =
testScope.runTest {
unlockDevice()
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
putDeviceToSleep(instantlyLockDevice = false)
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
wakeUpDevice()
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
}
@Test
@@ -435,20 +436,20 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
unlockDevice()
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
}
@Test
fun deviceGoesToSleep_withLockTimeout_staysOnLockscreen() =
testScope.runTest {
unlockDevice()
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
putDeviceToSleep(instantlyLockDevice = false)
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
// Pretend like the timeout elapsed and now lock the device.
lockDevice()
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
@@ -457,7 +458,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
setAuthMethod(AuthenticationMethodModel.Password)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(
to = upDestinationSceneKey,
)
@@ -466,7 +467,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
dismissIme()
emulatePendingTransitionProgress()
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
@@ -475,7 +476,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
setAuthMethod(AuthenticationMethodModel.Password)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(to = upDestinationSceneKey)
val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
@@ -495,7 +496,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
startPhoneCall()
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
- assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(to = upDestinationSceneKey)
val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
@@ -513,7 +514,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
testScope.runTest {
setAuthMethod(AuthenticationMethodModel.None)
introduceLockedSim()
- assertCurrentScene(SceneKey.Bouncer)
+ assertCurrentScene(Scenes.Bouncer)
}
@Test
@@ -523,7 +524,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
introduceLockedSim()
emulatePendingTransitionProgress(expectedVisible = true)
enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.None)
- assertCurrentScene(SceneKey.Gone)
+ assertCurrentScene(Scenes.Gone)
}
@Test
@@ -533,7 +534,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
introduceLockedSim()
emulatePendingTransitionProgress(expectedVisible = true)
enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
- assertCurrentScene(SceneKey.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
@@ -657,7 +658,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
assertThat(sceneContainerViewModel.currentScene.value).isEqualTo(to)
bouncerSceneJob =
- if (to == SceneKey.Bouncer) {
+ if (to == Scenes.Bouncer) {
testScope.backgroundScope.launch {
bouncerViewModel.authMethodViewModel.collect {
// Do nothing. Need this to turn this otherwise cold flow, hot.
@@ -688,7 +689,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
sceneInteractor.changeScene(to, "reason")
emulatePendingTransitionProgress(
- expectedVisible = to != SceneKey.Gone,
+ expectedVisible = to != Scenes.Gone,
)
}
@@ -715,7 +716,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
.that(deviceEntryInteractor.isUnlocked.value)
.isFalse()
- emulateUserDrivenTransition(SceneKey.Bouncer)
+ emulateUserDrivenTransition(Scenes.Bouncer)
fakeSceneDataSource.pause()
enterPin()
// This repository state is not changed by the AuthInteractor, it relies on
@@ -729,7 +730,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
/**
* Enters the correct PIN in the bouncer UI.
*
- * Asserts that the current scene is [SceneKey.Bouncer] and that the current bouncer UI is a PIN
+ * Asserts that the current scene is [Scenes.Bouncer] and that the current bouncer UI is a PIN
* before proceeding.
*
* Does not assert that the device is locked or unlocked.
@@ -737,7 +738,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private fun TestScope.enterPin() {
assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
.that(getCurrentSceneInUi())
- .isEqualTo(SceneKey.Bouncer)
+ .isEqualTo(Scenes.Bouncer)
val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
@@ -754,7 +755,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
/**
* Enters the correct PIN in the sim bouncer UI.
*
- * Asserts that the current scene is [SceneKey.Bouncer] and that the current bouncer UI is a PIN
+ * Asserts that the current scene is [Scenes.Bouncer] and that the current bouncer UI is a PIN
* before proceeding.
*
* Does not assert that the device is locked or unlocked.
@@ -764,7 +765,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
) {
assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
.that(getCurrentSceneInUi())
- .isEqualTo(SceneKey.Bouncer)
+ .isEqualTo(Scenes.Bouncer)
val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 1da3bc1aeda8..3d6619272dbe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -20,14 +20,14 @@ package com.android.systemui.scene.data.repository
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,12 +51,12 @@ class SceneContainerRepositoryTest : SysuiTestCase() {
assertThat(underTest.allSceneKeys())
.isEqualTo(
listOf(
- SceneKey.QuickSettings,
- SceneKey.Shade,
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
- SceneKey.Gone,
- SceneKey.Communal,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
+ Scenes.Gone,
+ Scenes.Communal,
)
)
}
@@ -66,17 +66,17 @@ class SceneContainerRepositoryTest : SysuiTestCase() {
testScope.runTest {
val underTest = kosmos.sceneContainerRepository
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- underTest.changeScene(SceneKey.Shade)
- assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ underTest.changeScene(Scenes.Shade)
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
}
@Test(expected = IllegalStateException::class)
fun changeScene_noSuchSceneInContainer_throws() {
- kosmos.sceneKeys = listOf(SceneKey.QuickSettings, SceneKey.Lockscreen)
+ kosmos.sceneKeys = listOf(Scenes.QuickSettings, Scenes.Lockscreen)
val underTest = kosmos.sceneContainerRepository
- underTest.changeScene(SceneKey.Shade)
+ underTest.changeScene(Scenes.Shade)
}
@Test
@@ -111,7 +111,7 @@ class SceneContainerRepositoryTest : SysuiTestCase() {
val underTest = kosmos.sceneContainerRepository
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
)
underTest.setTransitionState(transitionState)
val reflectedTransitionState by collectLastValue(underTest.transitionState)
@@ -120,8 +120,8 @@ class SceneContainerRepositoryTest : SysuiTestCase() {
val progress = MutableStateFlow(1f)
transitionState.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Shade,
progress = progress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt
index 9b0adb172e8d..6b5997fc21c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractorTest.kt
@@ -21,6 +21,8 @@ package com.android.systemui.scene.domain.interactor
import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -28,8 +30,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.panelExpansionInteractor
@@ -56,7 +57,7 @@ class PanelExpansionInteractorTest : SysuiTestCase() {
private val sceneInteractor = kosmos.sceneInteractor
private val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
)
private val fakeSceneDataSource = kosmos.fakeSceneDataSource
private val fakeShadeRepository = kosmos.fakeShadeRepository
@@ -76,19 +77,19 @@ class PanelExpansionInteractorTest : SysuiTestCase() {
setUnlocked(false)
val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)
- changeScene(SceneKey.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) }
+ changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) }
assertThat(panelExpansion).isEqualTo(1f)
- changeScene(SceneKey.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) }
+ changeScene(Scenes.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) }
assertThat(panelExpansion).isEqualTo(1f)
- changeScene(SceneKey.Shade) { assertThat(panelExpansion).isEqualTo(1f) }
+ changeScene(Scenes.Shade) { assertThat(panelExpansion).isEqualTo(1f) }
assertThat(panelExpansion).isEqualTo(1f)
- changeScene(SceneKey.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) }
+ changeScene(Scenes.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) }
assertThat(panelExpansion).isEqualTo(1f)
- changeScene(SceneKey.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
+ changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
assertThat(panelExpansion).isEqualTo(1f)
}
@@ -100,21 +101,19 @@ class PanelExpansionInteractorTest : SysuiTestCase() {
setUnlocked(true)
val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)
- changeScene(SceneKey.Gone) { assertThat(panelExpansion).isEqualTo(0f) }
+ changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) }
assertThat(panelExpansion).isEqualTo(0f)
- changeScene(SceneKey.Shade) { progress ->
- assertThat(panelExpansion).isEqualTo(progress)
- }
+ changeScene(Scenes.Shade) { progress -> assertThat(panelExpansion).isEqualTo(progress) }
assertThat(panelExpansion).isEqualTo(1f)
- changeScene(SceneKey.QuickSettings) {
+ changeScene(Scenes.QuickSettings) {
// Shade's already expanded, so moving to QS should also be 1f.
assertThat(panelExpansion).isEqualTo(1f)
}
assertThat(panelExpansion).isEqualTo(1f)
- changeScene(SceneKey.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
+ changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) }
assertThat(panelExpansion).isEqualTo(1f)
}
@@ -128,19 +127,19 @@ class PanelExpansionInteractorTest : SysuiTestCase() {
setUnlocked(false)
val panelExpansion by collectLastValue(underTest.legacyPanelExpansion)
- changeScene(SceneKey.Lockscreen)
+ changeScene(Scenes.Lockscreen)
assertThat(panelExpansion).isEqualTo(leet)
- changeScene(SceneKey.Bouncer)
+ changeScene(Scenes.Bouncer)
assertThat(panelExpansion).isEqualTo(leet)
- changeScene(SceneKey.Shade)
+ changeScene(Scenes.Shade)
assertThat(panelExpansion).isEqualTo(leet)
- changeScene(SceneKey.QuickSettings)
+ changeScene(Scenes.QuickSettings)
assertThat(panelExpansion).isEqualTo(leet)
- changeScene(SceneKey.Communal)
+ changeScene(Scenes.Communal)
assertThat(panelExpansion).isEqualTo(leet)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index db94c39e1cb1..f645f1cc4369 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -20,6 +20,7 @@ package com.android.systemui.scene.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
@@ -28,8 +29,7 @@ import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -67,23 +67,23 @@ class SceneInteractorTest : SysuiTestCase() {
fun changeScene() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- underTest.changeScene(SceneKey.Shade, "reason")
- assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ underTest.changeScene(Scenes.Shade, "reason")
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
}
@Test
fun changeScene_toGoneWhenUnl_doesNotThrow() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
runCurrent()
- underTest.changeScene(SceneKey.Gone, "reason")
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ underTest.changeScene(Scenes.Gone, "reason")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
}
@Test(expected = IllegalStateException::class)
@@ -91,18 +91,18 @@ class SceneInteractorTest : SysuiTestCase() {
testScope.runTest {
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- underTest.changeScene(SceneKey.Gone, "reason")
+ underTest.changeScene(Scenes.Gone, "reason")
}
@Test
fun sceneChanged_inDataSource() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- fakeSceneDataSource.changeScene(SceneKey.Shade)
+ fakeSceneDataSource.changeScene(Scenes.Shade)
- assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
}
@Test
@@ -111,7 +111,7 @@ class SceneInteractorTest : SysuiTestCase() {
val underTest = kosmos.sceneContainerRepository
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
)
underTest.setTransitionState(transitionState)
val reflectedTransitionState by collectLastValue(underTest.transitionState)
@@ -120,8 +120,8 @@ class SceneInteractorTest : SysuiTestCase() {
val progress = MutableStateFlow(1f)
transitionState.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Shade,
progress = progress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -153,27 +153,27 @@ class SceneInteractorTest : SysuiTestCase() {
val transitionTo by collectLastValue(underTest.transitioningTo)
assertThat(transitionTo).isNull()
- underTest.changeScene(SceneKey.Shade, "reason")
+ underTest.changeScene(Scenes.Shade, "reason")
assertThat(transitionTo).isNull()
val progress = MutableStateFlow(0f)
transitionState.value =
ObservableTransitionState.Transition(
fromScene = underTest.currentScene.value,
- toScene = SceneKey.Shade,
+ toScene = Scenes.Shade,
progress = progress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
- assertThat(transitionTo).isEqualTo(SceneKey.Shade)
+ assertThat(transitionTo).isEqualTo(Scenes.Shade)
progress.value = 0.5f
- assertThat(transitionTo).isEqualTo(SceneKey.Shade)
+ assertThat(transitionTo).isEqualTo(Scenes.Shade)
progress.value = 1f
- assertThat(transitionTo).isEqualTo(SceneKey.Shade)
+ assertThat(transitionTo).isEqualTo(Scenes.Shade)
- transitionState.value = ObservableTransitionState.Idle(SceneKey.Shade)
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Shade)
assertThat(transitionTo).isNull()
}
@@ -182,7 +182,7 @@ class SceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Shade)
+ ObservableTransitionState.Idle(Scenes.Shade)
)
val isTransitionUserInputOngoing by
collectLastValue(underTest.isTransitionUserInputOngoing)
@@ -197,8 +197,8 @@ class SceneInteractorTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Lockscreen,
progress = flowOf(0.5f),
isInitiatedByUserInput = true,
isUserInputOngoing = flowOf(true),
@@ -217,8 +217,8 @@ class SceneInteractorTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Lockscreen,
progress = flowOf(0.5f),
isInitiatedByUserInput = true,
isUserInputOngoing = flowOf(true),
@@ -232,8 +232,8 @@ class SceneInteractorTest : SysuiTestCase() {
transitionState.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Lockscreen,
progress = flowOf(0.6f),
isInitiatedByUserInput = true,
isUserInputOngoing = flowOf(false),
@@ -248,8 +248,8 @@ class SceneInteractorTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Lockscreen,
progress = flowOf(0.5f),
isInitiatedByUserInput = true,
isUserInputOngoing = flowOf(true),
@@ -261,7 +261,7 @@ class SceneInteractorTest : SysuiTestCase() {
assertThat(isTransitionUserInputOngoing).isTrue()
- transitionState.value = ObservableTransitionState.Idle(scene = SceneKey.Lockscreen)
+ transitionState.value = ObservableTransitionState.Idle(scene = Scenes.Lockscreen)
assertThat(isTransitionUserInputOngoing).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 4e1623661a58..cc66f8b2f387 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -24,6 +24,8 @@ import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -46,8 +48,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
@@ -136,42 +137,42 @@ class SceneContainerStartableTest : SysuiTestCase() {
val transitionStateFlow =
prepareState(
isDeviceUnlocked = true,
- initialSceneKey = SceneKey.Gone,
+ initialSceneKey = Scenes.Gone,
)
- assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentDesiredSceneKey).isEqualTo(Scenes.Gone)
assertThat(isVisible).isTrue()
underTest.start()
assertThat(isVisible).isFalse()
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Shade, "reason")
+ sceneInteractor.changeScene(Scenes.Shade, "reason")
transitionStateFlow.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Gone,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.Gone,
+ toScene = Scenes.Shade,
progress = flowOf(0.5f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
assertThat(isVisible).isTrue()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Shade)
- transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Shade)
+ transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Shade)
assertThat(isVisible).isTrue()
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
transitionStateFlow.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.Gone,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Gone,
progress = flowOf(0.5f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
assertThat(isVisible).isTrue()
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone)
- transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
+ transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
assertThat(isVisible).isFalse()
kosmos.headsUpNotificationRepository.hasPinnedHeadsUp.value = true
@@ -187,7 +188,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val isVisible by collectLastValue(sceneInteractor.isVisible)
prepareState(
isDeviceUnlocked = true,
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
isDeviceProvisioned = false,
isFrpActive = true,
)
@@ -214,7 +215,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
underTest.start()
runCurrent()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -223,14 +224,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
isDeviceUnlocked = true,
- initialSceneKey = SceneKey.Gone,
+ initialSceneKey = Scenes.Gone,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
underTest.start()
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -239,14 +240,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
isDeviceUnlocked = false,
- initialSceneKey = SceneKey.Bouncer,
+ initialSceneKey = Scenes.Bouncer,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
underTest.start()
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -255,14 +256,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
isBypassEnabled = true,
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
underTest.start()
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -271,16 +272,16 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
isBypassEnabled = false,
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
underTest.start()
// Authenticate using a passive auth method like face auth while bypass is disabled.
faceAuthRepository.isAuthenticated.value = true
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -291,19 +292,19 @@ class SceneContainerStartableTest : SysuiTestCase() {
prepareState(
isBypassEnabled = true,
authenticationMethod = AuthenticationMethodModel.Pin,
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
)
underTest.start()
runCurrent()
- sceneInteractor.changeScene(SceneKey.Shade, "switch to shade")
- transitionStateFlowValue.value = ObservableTransitionState.Idle(SceneKey.Shade)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
+ sceneInteractor.changeScene(Scenes.Shade, "switch to shade")
+ transitionStateFlowValue.value = ObservableTransitionState.Idle(Scenes.Shade)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Shade)
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
runCurrent()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Shade)
}
@Test
@@ -312,16 +313,16 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
isBypassEnabled = false,
- initialSceneKey = SceneKey.Bouncer,
+ initialSceneKey = Scenes.Bouncer,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
underTest.start()
// Authenticate using a passive auth method like face auth while bypass is disabled.
faceAuthRepository.isAuthenticated.value = true
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -330,13 +331,13 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
isDeviceUnlocked = false,
- initialSceneKey = SceneKey.Shade,
+ initialSceneKey = Scenes.Shade,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Shade)
underTest.start()
powerInteractor.setAsleepForTest()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -348,14 +349,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(sysUiState)
listOf(
- SceneKey.Gone,
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
- SceneKey.Shade,
- SceneKey.QuickSettings,
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
+ Scenes.Shade,
+ Scenes.QuickSettings,
)
.forEachIndexed { index, sceneKey ->
- if (sceneKey == SceneKey.Gone) {
+ if (sceneKey == Scenes.Gone) {
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
runCurrent()
}
@@ -379,15 +380,15 @@ class SceneContainerStartableTest : SysuiTestCase() {
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.None,
isLockscreenEnabled = false,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
underTest.start()
powerInteractor.setAwakeForTest()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -395,15 +396,15 @@ class SceneContainerStartableTest : SysuiTestCase() {
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.None,
isLockscreenEnabled = true,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
underTest.start()
powerInteractor.setAwakeForTest()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -411,14 +412,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
underTest.start()
powerInteractor.setAwakeForTest()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -426,12 +427,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
startsAwake = false
)
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
underTest.start()
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
@@ -439,14 +440,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
powerInteractor.setAwakeForTest()
runCurrent()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
}
@Test
fun collectFalsingSignals_onSuccessfulUnlock() =
testScope.runTest {
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
)
@@ -456,11 +457,11 @@ class SceneContainerStartableTest : SysuiTestCase() {
// Move around scenes without unlocking.
listOf(
- SceneKey.Shade,
- SceneKey.QuickSettings,
- SceneKey.Shade,
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
)
.forEach { sceneKey ->
sceneInteractor.changeScene(sceneKey, "reason")
@@ -471,17 +472,17 @@ class SceneContainerStartableTest : SysuiTestCase() {
// Changing to the Gone scene should report a successful unlock.
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
runCurrent()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
runCurrent()
verify(falsingCollector).onSuccessfulUnlock()
// Move around scenes without changing back to Lockscreen, shouldn't report another
// unlock.
listOf(
- SceneKey.Shade,
- SceneKey.QuickSettings,
- SceneKey.Shade,
- SceneKey.Gone,
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Gone,
)
.forEach { sceneKey ->
sceneInteractor.changeScene(sceneKey, "reason")
@@ -490,17 +491,17 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
// Changing to the Lockscreen scene shouldn't report a successful unlock.
- sceneInteractor.changeScene(SceneKey.Lockscreen, "reason")
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
runCurrent()
verify(falsingCollector, times(1)).onSuccessfulUnlock()
// Move around scenes without unlocking.
listOf(
- SceneKey.Shade,
- SceneKey.QuickSettings,
- SceneKey.Shade,
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
)
.forEach { sceneKey ->
sceneInteractor.changeScene(sceneKey, "reason")
@@ -509,7 +510,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
// Changing to the Gone scene should report a second successful unlock.
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
runCurrent()
verify(falsingCollector, times(2)).onSuccessfulUnlock()
}
@@ -518,7 +519,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
fun collectFalsingSignals_setShowingAod() =
testScope.runTest {
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
)
@@ -540,7 +541,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Password,
isDeviceUnlocked = false,
)
@@ -550,7 +551,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
bouncerInteractor.onImeHiddenByUser()
runCurrent()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -559,7 +560,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
kosmos.fakeKeyguardRepository.setAodAvailable(false)
runCurrent()
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
startsAwake = false,
@@ -607,7 +608,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
kosmos.fakeKeyguardRepository.setAodAvailable(true)
runCurrent()
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
)
@@ -652,7 +653,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
fun collectFalsingSignals_bouncerVisibility() =
testScope.runTest {
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
)
@@ -660,13 +661,13 @@ class SceneContainerStartableTest : SysuiTestCase() {
runCurrent()
verify(falsingCollector).onBouncerHidden()
- sceneInteractor.changeScene(SceneKey.Bouncer, "reason")
+ sceneInteractor.changeScene(Scenes.Bouncer, "reason")
runCurrent()
verify(falsingCollector).onBouncerShown()
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
runCurrent()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
runCurrent()
verify(falsingCollector, times(2)).onBouncerHidden()
}
@@ -677,7 +678,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
)
@@ -687,7 +688,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
runCurrent()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -697,7 +698,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Bouncer,
+ initialSceneKey = Scenes.Bouncer,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
)
@@ -706,7 +707,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false
runCurrent()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -716,7 +717,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
authenticationMethod = AuthenticationMethodModel.None,
isDeviceUnlocked = true,
isLockscreenEnabled = false,
@@ -726,7 +727,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false
runCurrent()
- assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -736,9 +737,9 @@ class SceneContainerStartableTest : SysuiTestCase() {
val transitionStateFlow =
prepareState(
isDeviceUnlocked = true,
- initialSceneKey = SceneKey.Gone,
+ initialSceneKey = Scenes.Gone,
)
- assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(currentDesiredSceneKey).isEqualTo(Scenes.Gone)
verify(windowController, never()).setNotificationShadeFocusable(anyBoolean())
underTest.start()
@@ -746,11 +747,11 @@ class SceneContainerStartableTest : SysuiTestCase() {
verify(windowController, times(1)).setNotificationShadeFocusable(false)
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Shade, "reason")
+ sceneInteractor.changeScene(Scenes.Shade, "reason")
transitionStateFlow.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Gone,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.Gone,
+ toScene = Scenes.Shade,
progress = flowOf(0.5f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -758,17 +759,17 @@ class SceneContainerStartableTest : SysuiTestCase() {
runCurrent()
verify(windowController, times(1)).setNotificationShadeFocusable(false)
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Shade)
- transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Shade)
+ transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Shade)
runCurrent()
verify(windowController, times(1)).setNotificationShadeFocusable(true)
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
transitionStateFlow.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.Gone,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Gone,
progress = flowOf(0.5f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -776,8 +777,8 @@ class SceneContainerStartableTest : SysuiTestCase() {
runCurrent()
verify(windowController, times(1)).setNotificationShadeFocusable(true)
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone)
- transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Gone)
+ transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone)
runCurrent()
verify(windowController, times(2)).setNotificationShadeFocusable(false)
}
@@ -787,7 +788,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
testScope.runTest {
val transitionStateFlow =
prepareState(
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
)
underTest.start()
runCurrent()
@@ -796,7 +797,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Bouncer,
+ toScene = Scenes.Bouncer,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -815,7 +816,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -834,7 +835,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Shade,
+ toScene = Scenes.Shade,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -853,7 +854,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -872,7 +873,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.QuickSettings,
+ toScene = Scenes.QuickSettings,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -891,7 +892,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
val transitionStateFlow =
prepareState(
isDeviceUnlocked = true,
- initialSceneKey = SceneKey.Gone,
+ initialSceneKey = Scenes.Gone,
)
underTest.start()
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
@@ -899,7 +900,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Bouncer,
+ toScene = Scenes.Bouncer,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -914,7 +915,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -929,7 +930,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Shade,
+ toScene = Scenes.Shade,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -944,7 +945,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -959,7 +960,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
clearInvocations(centralSurfaces)
emulateSceneTransition(
transitionStateFlow = transitionStateFlow,
- toScene = SceneKey.QuickSettings,
+ toScene = Scenes.QuickSettings,
verifyBeforeTransition = {
verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean())
},
@@ -978,12 +979,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val transitionStateFlow = prepareState()
underTest.start()
- emulateSceneTransition(transitionStateFlow, toScene = SceneKey.Bouncer)
- assertThat(currentScene).isNotEqualTo(SceneKey.Lockscreen)
+ emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer)
+ assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen)
kosmos.falsingManager.sendFalsingBelief()
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
}
private fun TestScope.emulateSceneTransition(
@@ -1033,7 +1034,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
}
- check(initialSceneKey != SceneKey.Gone || isDeviceUnlocked) {
+ check(initialSceneKey != Scenes.Gone || isDeviceUnlocked) {
"Cannot start on the Gone scene and have the device be locked at the same time."
}
@@ -1043,7 +1044,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
runCurrent()
val transitionStateFlow =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
)
sceneInteractor.setTransitionState(transitionStateFlow)
initialSceneKey?.let {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt
index ed4b1e6a43c9..32c0172071f6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt
@@ -53,9 +53,9 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
underTest.setDelegate(null)
- assertThat(currentScene).isNotEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isNotEqualTo(Scenes.Bouncer)
- underTest.changeScene(toScene = SceneKey.Bouncer)
+ underTest.changeScene(toScene = Scenes.Bouncer)
assertThat(currentScene).isEqualTo(initialSceneKey)
}
@@ -71,11 +71,11 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() {
fun currentScene_withDelegate_changesScenes() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isNotEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isNotEqualTo(Scenes.Bouncer)
- underTest.changeScene(toScene = SceneKey.Bouncer)
+ underTest.changeScene(toScene = Scenes.Bouncer)
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
@@ -83,8 +83,8 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- fakeSceneDataSource.changeScene(toScene = SceneKey.Bouncer)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Bouncer)
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 27ae8b60009c..7b0127e94fb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -30,7 +30,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
@@ -89,20 +89,20 @@ class SceneContainerViewModelTest : SysuiTestCase() {
fun sceneTransition() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- fakeSceneDataSource.changeScene(SceneKey.Shade)
+ fakeSceneDataSource.changeScene(Scenes.Shade)
- assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
}
@Test
fun canChangeScene_whenAllowed_switchingFromGone_returnsTrue() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
sceneContainerConfig.sceneKeys
.filter { it != currentScene }
@@ -117,9 +117,9 @@ class SceneContainerViewModelTest : SysuiTestCase() {
fun canChangeScene_whenAllowed_switchingFromLockscreen_returnsTrue() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
sceneContainerConfig.sceneKeys
.filter { it != currentScene }
@@ -135,15 +135,15 @@ class SceneContainerViewModelTest : SysuiTestCase() {
testScope.runTest {
falsingManager.setIsFalseTouch(true)
val currentScene by collectLastValue(underTest.currentScene)
- fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
sceneContainerConfig.sceneKeys
.filter { it != currentScene }
.filter {
// Moving to the Communal scene is not currently falsing protected.
- it != SceneKey.Communal
+ it != Scenes.Communal
}
.forEach { toScene ->
assertWithMessage("Protected scene $toScene not properly protected")
@@ -157,14 +157,14 @@ class SceneContainerViewModelTest : SysuiTestCase() {
testScope.runTest {
falsingManager.setIsFalseTouch(true)
val currentScene by collectLastValue(underTest.currentScene)
- fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
sceneContainerConfig.sceneKeys
.filter {
// Moving to the Communal scene is not currently falsing protected.
- it == SceneKey.Communal
+ it == Scenes.Communal
}
.forEach { toScene ->
assertWithMessage("Unprotected scene $toScene is incorrectly protected")
@@ -178,9 +178,9 @@ class SceneContainerViewModelTest : SysuiTestCase() {
testScope.runTest {
falsingManager.setIsFalseTouch(true)
val currentScene by collectLastValue(underTest.currentScene)
- fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
sceneContainerConfig.sceneKeys
.filter { it != currentScene }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index ec424b05fc06..d3fa3603d722 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -18,6 +18,8 @@ package com.android.systemui.shade
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -28,8 +30,7 @@ import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.CommandQueue
@@ -87,7 +88,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
runCurrent()
// THEN the shade remains collapsed and the post-collapse action ran
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Gone)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
verify(testRunnable, times(1)).run()
}
@@ -105,7 +106,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
runCurrent()
// THEN the shade remains expanded and the post-collapse action did not run
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Shade)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade)
assertThat(shadeInteractor.isAnyFullyExpanded.value).isTrue()
verify(testRunnable, never()).run()
}
@@ -122,7 +123,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
runCurrent()
// THEN the shade collapses back to lockscreen and the post-collapse action ran
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Lockscreen)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -137,7 +138,7 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
runCurrent()
// THEN the shade collapses back to lockscreen and the post-collapse action ran
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Gone)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
}
@Test
@@ -181,21 +182,21 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
private fun setDeviceEntered(isEntered: Boolean) {
setScene(
if (isEntered) {
- SceneKey.Gone
+ Scenes.Gone
} else {
- SceneKey.Lockscreen
+ Scenes.Lockscreen
}
)
assertThat(deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
}
private fun setCollapsed() {
- setScene(SceneKey.Gone)
+ setScene(Scenes.Gone)
assertThat(shadeInteractor.isAnyExpanded.value).isFalse()
}
private fun setShadeFullyExpanded() {
- setScene(SceneKey.Shade)
+ setScene(Scenes.Shade)
assertThat(shadeInteractor.isAnyFullyExpanded.value).isTrue()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt
index 1ef07facf8d1..bb40591335f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt
@@ -18,12 +18,12 @@ package com.android.systemui.shade.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,8 +53,8 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.QuickSettings,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.QuickSettings,
+ toScene = Scenes.Shade,
progress = MutableStateFlow(.1f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -76,8 +76,8 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.QuickSettings,
- toScene = SceneKey.Gone,
+ fromScene = Scenes.QuickSettings,
+ toScene = Scenes.Gone,
progress = MutableStateFlow(.1f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -99,8 +99,8 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.QuickSettings,
- toScene = SceneKey.Gone,
+ fromScene = Scenes.QuickSettings,
+ toScene = Scenes.Gone,
progress = MutableStateFlow(.1f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(true),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
index ec4da0405b6d..b66213330496 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
@@ -19,13 +19,14 @@ package com.android.systemui.shade.domain.interactor
import android.content.applicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shared.recents.utilities.Utilities
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -56,45 +57,45 @@ class ShadeBackActionInteractorImplTest : SysuiTestCase() {
@Test
fun animateCollapseQs_notOnQs() =
testScope.runTest {
- setScene(SceneKey.Shade)
+ setScene(Scenes.Shade)
underTest.animateCollapseQs(true)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Shade)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade)
}
@Test
fun animateCollapseQs_fullyCollapse_entered() =
testScope.runTest {
enterDevice()
- setScene(SceneKey.QuickSettings)
+ setScene(Scenes.QuickSettings)
underTest.animateCollapseQs(true)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Gone)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
}
@Test
fun animateCollapseQs_fullyCollapse_locked() =
testScope.runTest {
deviceEntryRepository.setUnlocked(false)
- setScene(SceneKey.QuickSettings)
+ setScene(Scenes.QuickSettings)
underTest.animateCollapseQs(true)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Lockscreen)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
}
@Test
fun animateCollapseQs_notFullyCollapse() =
testScope.runTest {
- setScene(SceneKey.QuickSettings)
+ setScene(Scenes.QuickSettings)
underTest.animateCollapseQs(false)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(SceneKey.Shade)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade)
}
private fun enterDevice() {
deviceEntryRepository.setUnlocked(true)
testScope.runCurrent()
- setScene(SceneKey.Gone)
+ setScene(Scenes.Gone)
}
private fun setScene(key: SceneKey) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index bf136cdd817e..4cd2c301d6ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.shade.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
@@ -27,8 +28,7 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.userRepository
import com.google.common.truth.Truth
@@ -67,8 +67,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.QuickSettings,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.QuickSettings,
+ toScene = Scenes.Shade,
progress = MutableStateFlow(.3f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -96,8 +96,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.QuickSettings,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.QuickSettings,
+ toScene = Scenes.Shade,
progress = progress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -120,8 +120,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.QuickSettings,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.QuickSettings,
+ toScene = Scenes.Shade,
progress = MutableStateFlow(.3f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -143,7 +143,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Shade)
+ ObservableTransitionState.Idle(Scenes.Shade)
)
sceneInteractor.setTransitionState(transitionState)
runCurrent()
@@ -161,7 +161,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.QuickSettings)
+ ObservableTransitionState.Idle(Scenes.QuickSettings)
)
sceneInteractor.setTransitionState(transitionState)
runCurrent()
@@ -174,7 +174,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun lockscreenShadeExpansion_idle_onScene() =
testComponent.runTest {
// GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.Shade
+ val key = Scenes.Shade
val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
val expansionAmount by collectLastValue(expansion)
@@ -191,13 +191,13 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun lockscreenShadeExpansion_idle_onDifferentScene() =
testComponent.runTest {
// GIVEN an expansion flow based on transitions to and from a scene
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.Shade)
val expansionAmount by collectLastValue(expansion)
// WHEN transition state is idle on a different scene
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Lockscreen)
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
)
sceneInteractor.setTransitionState(transitionState)
@@ -209,7 +209,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun lockscreenShadeExpansion_transitioning_toScene() =
testComponent.runTest {
// GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
+ val key = Scenes.QuickSettings
val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
val expansionAmount by collectLastValue(expansion)
@@ -218,7 +218,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Lockscreen,
toScene = key,
progress = progress,
isInitiatedByUserInput = false,
@@ -247,7 +247,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun lockscreenShadeExpansion_transitioning_fromScene() =
testComponent.runTest {
// GIVEN an expansion flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
+ val key = Scenes.QuickSettings
val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
val expansionAmount by collectLastValue(expansion)
@@ -257,7 +257,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
fromScene = key,
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
progress = progress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -290,8 +290,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Gone,
- toScene = SceneKey.QuickSettings,
+ fromScene = Scenes.Gone,
+ toScene = Scenes.QuickSettings,
progress = MutableStateFlow(.1f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -313,8 +313,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.QuickSettings,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.QuickSettings,
progress = MutableStateFlow(.1f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -331,7 +331,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
testComponent.runTest {
// GIVEN an expansion flow based on transitions to and from a scene
- val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
+ val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings)
val expansionAmount by collectLastValue(expansion)
// WHEN transition state is starting to between different scenes
@@ -339,8 +339,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Shade,
progress = progress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -368,7 +368,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun userInteracting_idle() =
testComponent.runTest {
// GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.Shade
+ val key = Scenes.Shade
val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
val interacting by collectLastValue(interactingFlow)
@@ -385,7 +385,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun userInteracting_transitioning_toScene_programmatic() =
testComponent.runTest {
// GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
+ val key = Scenes.QuickSettings
val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
val interacting by collectLastValue(interactingFlow)
@@ -394,7 +394,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Lockscreen,
toScene = key,
progress = progress,
isInitiatedByUserInput = false,
@@ -423,7 +423,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun userInteracting_transitioning_toScene_userInputDriven() =
testComponent.runTest {
// GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
+ val key = Scenes.QuickSettings
val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
val interacting by collectLastValue(interactingFlow)
@@ -432,7 +432,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
+ fromScene = Scenes.Lockscreen,
toScene = key,
progress = progress,
isInitiatedByUserInput = true,
@@ -461,7 +461,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun userInteracting_transitioning_fromScene_programmatic() =
testComponent.runTest {
// GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
+ val key = Scenes.QuickSettings
val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
val interacting by collectLastValue(interactingFlow)
@@ -471,7 +471,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
fromScene = key,
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
progress = progress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -499,7 +499,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun userInteracting_transitioning_fromScene_userInputDriven() =
testComponent.runTest {
// GIVEN an interacting flow based on transitions to and from a scene
- val key = SceneKey.QuickSettings
+ val key = Scenes.QuickSettings
val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
val interacting by collectLastValue(interactingFlow)
@@ -509,7 +509,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
fromScene = key,
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
progress = progress,
isInitiatedByUserInput = true,
isUserInputOngoing = flowOf(false),
@@ -537,7 +537,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
fun userInteracting_transitioning_toAndFromDifferentScenes() =
testComponent.runTest {
// GIVEN an interacting flow based on transitions to and from a scene
- val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+ val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, Scenes.Shade)
val interacting by collectLastValue(interactingFlow)
// WHEN transition state is starting to between different scenes
@@ -545,8 +545,8 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
- fromScene = SceneKey.Lockscreen,
- toScene = SceneKey.QuickSettings,
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.QuickSettings,
progress = MutableStateFlow(0f),
isInitiatedByUserInput = true,
isUserInputOngoing = flowOf(false),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index d655ade5cf2c..853b00d345bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -30,7 +30,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
@@ -125,7 +125,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
)
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
- assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(upTransitionSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -137,7 +137,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
)
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
- assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(upTransitionSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -148,9 +148,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.None
)
- sceneInteractor.changeScene(SceneKey.Lockscreen, "reason")
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
+ assertThat(upTransitionSceneKey).isEqualTo(Scenes.Lockscreen)
}
@Test
@@ -163,9 +163,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.None
)
runCurrent()
- sceneInteractor.changeScene(SceneKey.Gone, "reason")
+ sceneInteractor.changeScene(Scenes.Gone, "reason")
- assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
+ assertThat(upTransitionSceneKey).isEqualTo(Scenes.Gone)
}
@Test
@@ -206,7 +206,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
underTest.onContentClicked()
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
}
@Test
@@ -221,7 +221,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
underTest.onContentClicked()
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index efd8f000df41..47918c8c1b3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.coroutines.collectLastValue
@@ -28,8 +29,7 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationStackAppearanceViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
@@ -92,19 +92,19 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
testScope.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(scene = SceneKey.Gone)
+ ObservableTransitionState.Idle(scene = Scenes.Gone)
)
sceneInteractor.setTransitionState(transitionState)
val expandFraction by collectLastValue(appearanceViewModel.expandFraction)
assertThat(expandFraction).isEqualTo(0f)
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.Shade, "reason")
+ sceneInteractor.changeScene(Scenes.Shade, "reason")
val transitionProgress = MutableStateFlow(0f)
transitionState.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Gone,
- toScene = SceneKey.Shade,
+ fromScene = Scenes.Gone,
+ toScene = Scenes.Shade,
progress = transitionProgress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -117,7 +117,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
assertThat(expandFraction).isWithin(0.01f).of(progress)
}
- fakeSceneDataSource.unpause(expectedScene = SceneKey.Shade)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.Shade)
assertThat(expandFraction).isWithin(0.01f).of(1f)
}
@@ -126,7 +126,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
testScope.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(scene = SceneKey.Lockscreen)
+ ObservableTransitionState.Idle(scene = Scenes.Lockscreen)
)
sceneInteractor.setTransitionState(transitionState)
val expandFraction by collectLastValue(appearanceViewModel.expandFraction)
@@ -138,19 +138,19 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
testScope.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(scene = SceneKey.Shade)
+ ObservableTransitionState.Idle(scene = Scenes.Shade)
)
sceneInteractor.setTransitionState(transitionState)
val expandFraction by collectLastValue(appearanceViewModel.expandFraction)
assertThat(expandFraction).isEqualTo(1f)
fakeSceneDataSource.pause()
- sceneInteractor.changeScene(SceneKey.QuickSettings, "reason")
+ sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
val transitionProgress = MutableStateFlow(0f)
transitionState.value =
ObservableTransitionState.Transition(
- fromScene = SceneKey.Shade,
- toScene = SceneKey.QuickSettings,
+ fromScene = Scenes.Shade,
+ toScene = Scenes.QuickSettings,
progress = transitionProgress,
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
@@ -163,7 +163,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
assertThat(expandFraction).isEqualTo(1f)
}
- fakeSceneDataSource.unpause(expectedScene = SceneKey.QuickSettings)
+ fakeSceneDataSource.unpause(expectedScene = Scenes.QuickSettings)
assertThat(expandFraction).isEqualTo(1f)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 7f5a658587f3..0de15b8db665 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -21,13 +21,13 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -278,8 +278,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
)
)
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -391,8 +391,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
// Move to glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index c01f1c71fdd3..8aa0e3fc4d23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -35,10 +35,10 @@ import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeAnimationRepository
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -76,7 +76,7 @@ class ActivityStarterImplTest : SysuiTestCase() {
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var keyguardViewMediator: KeyguardViewMediator
@Mock private lateinit var shadeController: ShadeController
- @Mock private lateinit var shadeViewController: ShadeViewController
+ @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
@Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@@ -105,7 +105,7 @@ class ActivityStarterImplTest : SysuiTestCase() {
Lazy { biometricUnlockController },
Lazy { keyguardViewMediator },
Lazy { shadeController },
- Lazy { shadeViewController },
+ commandQueue,
shadeAnimationInteractor,
Lazy { statusBarKeyguardViewManager },
Lazy { notifShadeWindowController },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 6390e82321f0..db4d42f4c864 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -53,6 +53,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -142,6 +143,12 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
}
}
+ @Override
+ public void SysuiSetup() throws Exception {
+ super.SysuiSetup();
+ mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+ }
+
@Test
public void testShowNotification_addsEntry() {
final BaseHeadsUpManager alm = createHeadsUpManager();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
index ec23f76935d6..c032d7cb06b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
@@ -39,6 +39,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -136,6 +137,8 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {
@Before
public void setUp() {
+ mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+
when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
final AccessibilityManagerWrapper accessibilityMgr =
mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
index 243aab24b07d..dcf635e622f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -32,8 +32,8 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.mediaController
import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.mediaOutputActionsInteractor
import com.android.systemui.volume.mediaOutputInteractor
-import com.android.systemui.volume.panel.mediaOutputActionsInteractor
import com.android.systemui.volume.panel.volumePanelViewModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
new file mode 100644
index 000000000000..5fe74aa6817f
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+xmlns:app="http://schemas.android.com/apk/res-auto"
+xmlns:tools="http://schemas.android.com/tools"
+android:layout_width="match_parent"
+android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/background"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@string/biometric_dialog_empty_space_description"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <View
+ android:id="@+id/panel"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="?android:attr/colorBackgroundFloating"
+ android:clickable="true"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:paddingHorizontal="16dp"
+ android:paddingVertical="16dp"
+ android:visibility="visible"
+ app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
+ app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
+ app:layout_constraintStart_toStartOf="@+id/leftGuideline"
+ app:layout_constraintTop_toTopOf="@+id/topGuideline" />
+
+ <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+ android:id="@+id/biometric_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.8"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:srcCompat="@tools:sample/avatars" />
+
+ <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+ android:id="@+id/biometric_icon_overlay"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY"
+ app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+ app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+ app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:fillViewport="true"
+ android:padding="16dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
+ app:layout_constraintEnd_toStartOf="@+id/midGuideline"
+ app:layout_constraintStart_toStartOf="@id/leftGuideline"
+ app:layout_constraintTop_toTopOf="@+id/topGuideline">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/innerConstraint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/logo"
+ android:layout_width="@dimen/biometric_auth_icon_size"
+ android:layout_height="@dimen/biometric_auth_icon_size"
+ android:layout_gravity="center"
+ android:scaleType="fitXY"
+ android:visibility="visible"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <LinearLayout
+ android:id="@+id/customized_view_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingHorizontal="0dp"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <Space
+ android:id="@+id/space_above_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/biometric_prompt_space_above_content"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthCredential.Title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingHorizontal="0dp"
+ android:textAlignment="viewStart"
+ app:layout_constraintBottom_toTopOf="@+id/subtitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/logo"
+ app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintVertical_chainStyle="packed" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthCredential.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingHorizontal="0dp"
+ android:textAlignment="viewStart"
+ app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/title" />
+
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthCredential.Description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingHorizontal="0dp"
+ android:textAlignment="viewStart"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <TextView
+ android:id="@+id/logo_description"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:paddingLeft="8dp"
+ app:layout_constraintBottom_toBottomOf="@+id/logo"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/logo"
+ app:layout_constraintTop_toTopOf="@+id/logo" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/contentBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="description, customized_view_container" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </ScrollView>
+
+ <TextView
+ android:id="@+id/indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:accessibilityLiveRegion="polite"
+ android:fadingEdge="horizontal"
+ android:gravity="center_horizontal"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:scrollHorizontally="true"
+ android:textColor="@color/biometric_dialog_gray"
+ android:textSize="12sp"
+ app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
+ app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+ app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+ app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <!-- Negative Button, reserved for app -->
+ <Button
+ android:id="@+id/button_negative"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
+ app:layout_constraintStart_toStartOf="@+id/scrollView" />
+
+ <!-- Cancel Button, replaces negative button when biometric is accepted -->
+ <Button
+ android:id="@+id/button_cancel"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:text="@string/cancel"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
+ app:layout_constraintStart_toStartOf="@+id/scrollView" />
+
+ <!-- "Use Credential" Button, replaces if device credential is allowed -->
+ <Button
+ android:id="@+id/button_use_credential"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
+ app:layout_constraintStart_toStartOf="@+id/scrollView" />
+
+ <!-- Positive Button -->
+ <Button
+ android:id="@+id/button_confirm"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginRight="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_confirm"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
+ app:layout_constraintEnd_toEndOf="@+id/scrollView"
+ tools:visibility="invisible" />
+
+ <!-- Try Again Button -->
+ <Button
+ android:id="@+id/button_try_again"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginRight="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_try_again"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
+ app:layout_constraintEnd_toEndOf="@+id/scrollView" />
+
+ <!-- Guidelines for setting panel border -->
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/topBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="scrollView" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/buttonBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/leftGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/rightGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/midGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_begin="406dp" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/bottomGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/topGuideline"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
new file mode 100644
index 000000000000..5b30dfb77342
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/background"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@string/biometric_dialog_empty_space_description"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <View
+ android:id="@+id/panel"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="?android:attr/colorBackgroundFloating"
+ android:clickable="true"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:paddingHorizontal="16dp"
+ android:paddingVertical="16dp"
+ android:visibility="visible"
+ app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
+ app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
+ app:layout_constraintStart_toStartOf="@+id/leftGuideline"
+ app:layout_constraintTop_toTopOf="@+id/topBarrier" />
+
+ <Button
+ android:id="@+id/button_negative"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/panel"
+ app:layout_constraintStart_toStartOf="@+id/panel" />
+
+ <Button
+ android:id="@+id/button_cancel"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:text="@string/cancel"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/panel"
+ app:layout_constraintStart_toStartOf="@+id/panel" />
+
+ <Button
+ android:id="@+id/button_use_credential"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/panel"
+ app:layout_constraintStart_toStartOf="@+id/panel" />
+
+ <Button
+ android:id="@+id/button_confirm"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginRight="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_confirm"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/panel"
+ app:layout_constraintEnd_toEndOf="@+id/panel"
+ tools:visibility="invisible" />
+
+ <Button
+ android:id="@+id/button_try_again"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="8dp"
+ android:layout_marginRight="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_try_again"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/panel"
+ app:layout_constraintEnd_toEndOf="@+id/panel" />
+
+ <!-- Negative Button, reserved for app -->
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:fillViewport="true"
+ android:padding="16dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
+ app:layout_constraintEnd_toEndOf="@id/panel"
+ app:layout_constraintStart_toStartOf="@id/panel"
+ app:layout_constraintTop_toTopOf="@+id/topGuideline"
+ app:layout_constraintVertical_bias="1.0">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/innerConstraint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/logo"
+ android:layout_width="@dimen/biometric_auth_icon_size"
+ android:layout_height="@dimen/biometric_auth_icon_size"
+ android:layout_gravity="center"
+ android:scaleType="fitXY"
+ android:visibility="visible"
+ app:layout_constraintBottom_toTopOf="@+id/logo_description"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <LinearLayout
+ android:id="@+id/customized_view_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+
+ <Space
+ android:id="@+id/space_above_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/biometric_prompt_space_above_content"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthCredential.Title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ app:layout_constraintBottom_toTopOf="@+id/subtitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/logo_description" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthCredential.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/title" />
+
+ <TextView
+ android:id="@+id/logo_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ app:layout_constraintBottom_toTopOf="@+id/title"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/logo" />
+
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthCredential.Description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/contentBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="description, customized_view_container" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </ScrollView>
+
+ <!-- Cancel Button, replaces negative button when biometric is accepted -->
+ <TextView
+ android:id="@+id/indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:accessibilityLiveRegion="polite"
+ android:fadingEdge="horizontal"
+ android:gravity="center_horizontal"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:scrollHorizontally="true"
+ android:textColor="@color/biometric_dialog_gray"
+ android:textSize="12sp"
+ app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
+ app:layout_constraintEnd_toEndOf="@+id/panel"
+ app:layout_constraintStart_toStartOf="@+id/panel"
+ app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <!-- "Use Credential" Button, replaces if device credential is allowed -->
+
+ <!-- Positive Button -->
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/topBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="scrollView" />
+
+ <!-- Try Again Button -->
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/buttonBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
+
+ <!-- Guidelines for setting panel border -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/leftGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/rightGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/bottomGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/topGuideline"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.25171" />
+
+ <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+ android:id="@+id/biometric_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.8"
+ tools:srcCompat="@tools:sample/avatars" />
+
+ <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+ android:id="@+id/biometric_icon_overlay"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY"
+ app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+ app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+ app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index 0ecdcfcbdd83..74292b4edf0a 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -1,27 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-xmlns:app="http://schemas.android.com/apk/res-auto"
-xmlns:tools="http://schemas.android.com/tools"
-android:layout_width="match_parent"
-android:layout_height="match_parent">
-
- <ImageView
- android:id="@+id/logo"
- android:layout_width="@dimen/biometric_auth_icon_size"
- android:layout_height="@dimen/biometric_auth_icon_size"
- android:layout_gravity="center"
- android:scaleType="fitXY"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/logo_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:singleLine="true"
- android:marqueeRepeatLimit="1"
- android:ellipsize="marquee"
- android:visibility="gone"/>
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<ImageView
android:id="@+id/background"
@@ -47,7 +29,7 @@ android:layout_height="match_parent">
app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
app:layout_constraintStart_toStartOf="@+id/leftGuideline"
- app:layout_constraintTop_toTopOf="@+id/title" />
+ app:layout_constraintTop_toTopOf="@+id/topBarrier" />
<com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
android:id="@+id/biometric_icon"
@@ -69,90 +51,133 @@ android:layout_height="match_parent">
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
- app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="@+id/biometric_icon"
- app:layout_constraintTop_toTopOf="@+id/biometric_icon"
- app:layout_constraintVertical_bias="0.0" />
+ app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:singleLine="true"
- android:marqueeRepeatLimit="1"
- android:ellipsize="marquee"
- style="@style/TextAppearance.AuthCredential.Title"
- app:layout_constraintBottom_toTopOf="@+id/subtitle"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
+ android:fillViewport="true"
+ android:padding="16dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
+ app:layout_constraintEnd_toEndOf="@id/rightGuideline"
+ app:layout_constraintStart_toStartOf="@id/leftGuideline"
+ app:layout_constraintTop_toTopOf="@+id/topGuideline"
+ app:layout_constraintVertical_bias="1.0">
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:singleLine="true"
- android:marqueeRepeatLimit="1"
- android:ellipsize="marquee"
- style="@style/TextAppearance.AuthCredential.Subtitle"
- app:layout_constraintBottom_toTopOf="@+id/description"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/innerConstraint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <Space
- android:id="@+id/space_above_content"
- android:layout_width="match_parent"
- android:layout_height="@dimen/biometric_prompt_space_above_content"
- android:visibility="gone"
- app:layout_constraintTop_toBottomOf="@+id/subtitle"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel"/>
+ <ImageView
+ android:id="@+id/logo"
+ android:layout_width="@dimen/biometric_auth_icon_size"
+ android:layout_height="@dimen/biometric_auth_icon_size"
+ android:layout_gravity="center"
+ android:scaleType="fitXY"
+ android:visibility="visible"
+ app:layout_constraintBottom_toTopOf="@+id/logo_description"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
- <ScrollView
- android:id="@+id/customized_view_container"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:fillViewport="true"
- android:fadeScrollbars="false"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
- android:scrollbars="vertical"
- android:visibility="gone"
- app:layout_constraintTop_toBottomOf="@+id/space_above_content"
- app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel"/>
+ <LinearLayout
+ android:id="@+id/customized_view_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle" />
- <TextView
- android:id="@+id/description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="24dp"
- android:scrollbars="vertical"
- android:gravity="@integer/biometric_dialog_text_gravity"
- style="@style/TextAppearance.AuthCredential.Description"
- app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
+ <Space
+ android:id="@+id/space_above_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/biometric_prompt_space_above_content"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthCredential.Title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ app:layout_constraintBottom_toTopOf="@+id/subtitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/logo_description" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthCredential.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/title" />
+
+ <TextView
+ android:id="@+id/logo_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ app:layout_constraintBottom_toTopOf="@+id/title"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/logo" />
+
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthCredential.Description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/contentBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="description, customized_view_container" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </ScrollView>
<TextView
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:gravity="center_horizontal"
- android:textColor="@color/biometric_dialog_gray"
- android:textSize="12sp"
android:accessibilityLiveRegion="polite"
+ android:fadingEdge="horizontal"
+ android:gravity="center_horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
- android:fadingEdge="horizontal"
+ android:textColor="@color/biometric_dialog_gray"
+ android:textSize="12sp"
+ app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
app:layout_constraintEnd_toEndOf="@+id/panel"
- app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/panel"
- app:layout_constraintTop_toBottomOf="@+id/biometric_icon" />
+ app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintVertical_bias="0.0" />
<!-- Negative Button, reserved for app -->
<Button
@@ -230,6 +255,22 @@ android:layout_height="match_parent">
app:layout_constraintEnd_toEndOf="@+id/panel" />
<!-- Guidelines for setting panel border -->
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/topBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="scrollView" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/buttonBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
+
<androidx.constraintlayout.widget.Guideline
android:id="@+id/leftGuideline"
android:layout_width="wrap_content"
@@ -251,4 +292,11 @@ android:layout_height="match_parent">
android:orientation="horizontal"
app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/topGuideline"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.25171" />
+
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
index e7590746207d..984210906e68 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
@@ -70,7 +70,7 @@
android:layout_height="@dimen/biometric_prompt_space_above_content"
android:visibility="gone" />
- <ScrollView
+ <LinearLayout
android:id="@+id/customized_view_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
index 61f69c04c174..f6042e467987 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
@@ -128,7 +128,8 @@
android:layout_marginStart="49dp"
android:layout_marginEnd="49dp"
android:overScrollMode="never"
- android:layout_marginBottom="16dp">
+ android:layout_marginBottom="16dp"
+ android:scrollbars="none">
<LinearLayout
android:id="@+id/keyboard_shortcuts_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 68c8dd96d188..d8d298573d04 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -19,11 +19,11 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/half_shelf_dialog"
android:orientation="vertical"
- android:layout_width="wrap_content"
+ android:layout_width="@dimen/large_dialog_width"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
- android:paddingStart="4dp"
- android:paddingEnd="4dp">
+ android:paddingLeft="@dimen/dialog_side_padding"
+ android:paddingRight="@dimen/dialog_side_padding">
<LinearLayout
android:id="@+id/half_shelf"
diff --git a/packages/SystemUI/res/layout/scene_window_root.xml b/packages/SystemUI/res/layout/scene_window_root.xml
index bb8de4c32e76..0dcd15b429c1 100644
--- a/packages/SystemUI/res/layout/scene_window_root.xml
+++ b/packages/SystemUI/res/layout/scene_window_root.xml
@@ -24,7 +24,7 @@
android:id="@+id/scene_window_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="false">
+ android:fitsSystemWindows="true">
<include layout="@layout/super_notification_shade"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 4002517b2cd4..515ef619141b 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Voeg meer legstukke by"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Langdruk om legstukke te pasmaak"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Pasmaak legstukke"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Wysig legstuk"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Verwyder"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Voeg legstuk by"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Lui"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibreer"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Demp"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Saai uit"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Onbeskikbaar omdat luitoon gedemp is"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om te ontdemp."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om op vibreer te stel. Toeganklikheidsdienste kan dalk gedemp wees."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tik om op vibreer te stel."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tik om te demp."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Geraasbeheer"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om luiermodus te verander"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"demp"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ontdemp"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Aan/af-kieslys"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Bladsy <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Sluitskerm"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Sien versorgingstappe"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Sien versorgingstappe"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Prop jou toestel uit"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Maak notas"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Maak notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Deel tans oudio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitsaai"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hou op om <xliff:g id="APP_NAME">%1$s</xliff:g> uit te saai?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"As jy <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitsaai of die uitvoer verander, sal jou huidige uitsending stop"</string>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 662aa717ffc1..1c9a79414ff1 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Af"</item>
<item msgid="578444932039713369">"Aan"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Onbeskikbaar"</item>
<item msgid="8707481475312432575">"Af"</item>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index ebc20027209f..9763ff2ae468 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ተጨማሪ ምግብሮችን ያክሉ"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ምግብሮችን ለማበጀት በረጅሙ ይጫኑ"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ምግብሮችን አብጅ"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ምግብርን አርትዕ"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"አስወግድ"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ምግብር አክል"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"ጥሪ"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ንዘር"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"ድምጸ-ከል አድርግ"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"የጥሪ ድምጽ ስለተዘጋ አይገኝም"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ።"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ።"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"የጫጫታ መቆጣጠሪያ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"የደዋይ ሁነታን ለመቀየር መታ ያድርጉ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ድምጸ-ከል አድርግ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ድምጸ-ከልን አንሳ"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"የኃይል ምናሌ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ገፅ <xliff:g id="ID_1">%1$d</xliff:g> ከ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ማያ ገፅ ቁልፍ"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"የእንክብካቤ ደረጃዎችን ይመልከቱ"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"የእንክብካቤ ደረጃዎችን ይመልከቱ"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"መሣሪያዎን ይንቀሉ"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>፣ <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"የማስታወሻ አያያዝ"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"የማስታወሻ አያያዝ፣ <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ኦዲዮ በማጋራት ላይ"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"በማሰራጨት ላይ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን ማሰራጨት ይቁም?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>ን ካሰራጩ ወይም ውፅዓትን ከቀየሩ የአሁኑ ስርጭትዎ ይቆማል"</string>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index e5d68d985792..3fb24b983162 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ጠፍቷል"</item>
<item msgid="578444932039713369">"በርቷል"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"አይገኝም"</item>
<item msgid="8707481475312432575">"ጠፍቷል"</item>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ec72578f9fc3..1b7e3037103f 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"إضافة المزيد من التطبيقات المصغّرة"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"اضغط مع الاستمرار لتخصيص التطبيقات المصغّرة."</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"تخصيص التطبيقات المصغَّرة"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"تعديل التطبيق المصغَّر"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"إزالة"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"إضافة تطبيق مصغّر"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"استصدار رنين"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"اهتزاز"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"كتم الصوت"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"البثّ"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"يتعذّر التغيير بسبب كتم صوت الرنين."</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏%1$s. انقر لإلغاء التجاهل."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"‏%1$s. انقر للتعيين على الاهتزاز."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"‏%1$s. انقر لكتم الصوت."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"التحكُّم في مستوى الضجيج"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"انقر لتغيير وضع الرنين."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"كتم الصوت"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"إعادة الصوت"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"قائمة زر التشغيل"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"الصفحة <xliff:g id="ID_1">%1$d</xliff:g> من <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"شاشة القفل"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"الاطّلاع على خطوات العناية"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"الاطّلاع على خطوات العناية"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"افصِل جهازك"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"تدوين الملاحظات"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"تدوين الملاحظات في \"<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>\""</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"تتم مشاركة الصوت"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"البث"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"هل تريد إيقاف بث تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>؟"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"إذا أجريت بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g> أو غيَّرت جهاز الإخراج، سيتوقَف البث الحالي."</string>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index 856ae1db8fa1..cf050ac26473 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"الميزة غير مفعّلة"</item>
<item msgid="578444932039713369">"الميزة مفعّلة"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"الميزة غير متاحة"</item>
<item msgid="8707481475312432575">"الميزة غير مفعّلة"</item>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index f66650b41451..429f03ed3a63 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"অধিক ৱিজেট যোগ দিয়ক"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ৱিজেট কাষ্টমাইজ কৰিবলৈ দীঘলীয়াকৈ টিপক"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ৱিজেট কাষ্টমাইজ কৰক"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ৱিজেট সম্পাদনা কৰক"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"আঁতৰাওক"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ৱিজেট যোগ দিয়ক"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"ৰিং"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"কম্পন"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"মিউট"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"কাষ্ট কৰক"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ৰিং মিউট কৰি থোৱাৰ বাবে উপলব্ধ নহয়"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। আনমিউট কৰিবৰ বাবে টিপক।"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পনৰ বাবে টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট কৰিবলৈ টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। কম্পন অৱস্থাত ছেট কৰিবলৈ টিপক।"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। মিউট কৰিবলৈ টিপক।"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"কোলাহল নিয়ন্ত্ৰণ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ৰিংগাৰ ম’ড সলনি কৰিবলৈ টিপক"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট কৰক"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট কৰক"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"পাৱাৰ মেনু"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>ৰ পৃষ্ঠা <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্ৰীন"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"যত্ন লোৱাৰ পদক্ষেপসমূহ চাওক"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"যত্ন লোৱাৰ পদক্ষেপসমূহ চাওক"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"আপোনাৰ ডিভাইচটো আনপ্লাগ কৰক"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"টোকা গ্ৰহণ কৰা"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"টোকা গ্ৰহণ কৰা, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"সম্প্ৰচাৰণ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰা বন্ধ কৰিবনে?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"যদি আপুনি <xliff:g id="SWITCHAPP">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰে অথবা আউটপুট সলনি কৰে, তেন্তে, আপোনাৰ বৰ্তমানৰ সম্প্ৰচাৰ বন্ধ হৈ যাব"</string>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index a9c3e3b997b7..f4268ed17ff5 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"অফ আছে"</item>
<item msgid="578444932039713369">"অন কৰা আছে"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"উপলব্ধ নহয়"</item>
<item msgid="8707481475312432575">"অফ আছে"</item>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 70484414a4cb..639cbbc4cef1 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Vidcetlər əlavə edin"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Basıb saxlayaraq vidcetləri fərdiləşdirin"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Vidcetləri fərdiləşdirin"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Vidceti redaktə edin"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Silin"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidcet əlavə edin"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zəng"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrasiya"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Susdurun"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Yayım"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zəng səssiz edildiyi üçün əlçatan deyil"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Səsli etmək üçün tıklayın."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Vibrasiyanı ayarlamaq üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Vibrasiyanı ayarlamaq üçün klikləyin."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Səssiz etmək üçün klikləyin."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Səs-küy idarəsi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zəng rejimini dəyişmək üçün toxunun"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"susdurun"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"səssiz rejimdən çıxarın"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Qidalanma düyməsi menyusu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> səhifədən <xliff:g id="ID_1">%1$d</xliff:g> səhifə"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran kilidi"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ehtiyat tədbiri mərhələlərinə baxın"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ehtiyat tədbiri mərhələlərinə baxın"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Cihazınızı ayırın"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Qeydgötürmə"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Qeydgötürmə, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audio paylaşılır"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayım"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinin yayımlanması dayandırılsın?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlasanız və ya nəticəni dəyişsəniz, cari yayımınız dayandırılacaq"</string>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index d973e4ead3c2..eeb81ccf42e1 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Deaktiv"</item>
<item msgid="578444932039713369">"Aktiv"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Əlçatan deyil"</item>
<item msgid="8707481475312432575">"Deaktiv"</item>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index a598d8f3733b..e97dbec9a690 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte još vidžeta"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dugi pritisak za prilagođavanje vidžeta"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodi vidžete"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Izmeni vidžet"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj vidžet"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Aktiviraj zvono"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriraj"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Isključi zvuk"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Prebacivanje"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvuk isključen"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste podesili na vibraciju. Zvuk usluga pristupačnosti će možda biti isključen."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da biste podesili na vibraciju."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dodirnite da biste isključili zvuk."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kontrola šuma"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promenili režim zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meni dugmeta za uključivanje"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. strana od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključan ekran"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Pogledajte upozorenja"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte upozorenja"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Isključite uređaj"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Pravljenje beležaka"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pravljenje beležaka, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Deli se zvuk"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitovanje"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite da zaustavite emitovanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitujete aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promenite izlaz, aktuelno emitovanje će se zaustaviti"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index 32051ef19743..217d99975d3f 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Isključeno"</item>
<item msgid="578444932039713369">"Uključeno"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nedostupno"</item>
<item msgid="8707481475312432575">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 2b137fb63258..3b06d05e76f5 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Дадаць іншыя віджэты"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Доўга націскайце, каб наладзіць віджэты"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Наладзіць віджэты"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Змяніць віджэт"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Выдаліць"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Дадаць віджэт"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Званок"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вібрацыя"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Гук выключаны"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Трансляцыя"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недаступна, бо выключаны гук выклікаў"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дакраніцеся, каб уключыць гук."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дакраніцеся, каб уключыць вібрацыю. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Дакраніцеся, каб уключыць вібрацыю."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дакраніцеся, каб адключыць гук"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Кантроль шуму"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Націсніце, каб змяніць рэжым званка"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"выключыць гук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"уключыць гук"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню кнопкі сілкавання"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Старонка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Экран блакіроўкі"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Глядзець паэтапную дапамогу"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Глядзець паэтапную дапамогу"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Адключыце прыладу"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Стварэнне нататак"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Стварэнне нататак, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Ідзе абагульванне аўдыя"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Перадача даных"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Спыніць трансляцыю праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Пры пераключэнні на праграму \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" ці змяненні вываду бягучая трансляцыя спыняецца"</string>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index e71c29bd15de..717e4c9a9c39 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Выключана"</item>
<item msgid="578444932039713369">"Уключана"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Недаступна"</item>
<item msgid="8707481475312432575">"Выключана"</item>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index aa3da9caa6f9..643ef9cecbf1 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Добавете още приспособления"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Натиснете продължително за персонализ. на приспос."</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Персонализиране на приспособленията"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Редактиране на приспособлението"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Премахване"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Добавяне на приспособление"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Позвъняване"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибриране"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Без звук"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Предаване"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Не е налице, защото звъненето е спряно"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Докоснете, за да включите отново звука."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Докоснете, за да зададете вибриране. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Докоснете, за да зададете вибриране."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Докоснете, за да заглушите звука."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Управление на шума"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Докоснете, за да промените режима на звънене"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"спиране"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"пускане"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню за включване/изключване"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> от <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заключен екран"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Вижте стъпките, които да предприемете"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Вижте стъпките, които да предприемете"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Изключете устройството си"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Водене на бележки"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Водене на бележки, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Споделя аудио"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Излъчване"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се спре ли предаването на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако предавате <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените изхода, текущото ви предаване ще бъде прекратено"</string>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index 24b41d23e900..58fa82bbc77a 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Изкл."</item>
<item msgid="578444932039713369">"Вкл."</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Не е налице"</item>
<item msgid="8707481475312432575">"Изкл."</item>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 12e24c0a3bd3..9a2f040c7e7e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"আরও উইজেট যোগ করুন"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"উইজেট কাস্টমাইজ করতে বেশিক্ষণ প্রেস করুন"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"উইজেট কাস্টমাইজ করুন"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"উইজেট এডিট করুন"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"সরান"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"উইজেট যোগ করুন"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"রিং"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ভাইব্রেট"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"মিউট"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"কাস্ট করুন"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"রিং মিউট করা হয়েছে বলে উপলভ্য নেই"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। সশব্দ করতে আলতো চাপুন।"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। ভাইব্রেট করতে ট্যাপ করুন।"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। মিউট করতে ট্যাপ করুন।"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"আশপাশের আওয়াজ কন্ট্রোল করা"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"রিঙ্গার মোড পরিবর্তন করতে ট্যাপ করুন"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট করুন"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট করুন"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"পাওয়ার মেনু"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>টির মধ্যে <xliff:g id="ID_1">%1$d</xliff:g> নং পৃষ্ঠা"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্রিন"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ডিভাইস রক্ষণাবেক্ষণের ধাপগুলি দেখুন"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ডিভাইস রক্ষণাবেক্ষণের ধাপগুলি দেখুন"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"আপনার ডিভাইস আনপ্লাগ করা"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"নোট নেওয়া"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"নোট নেওয়া, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"অডিও শেয়ার করা হচ্ছে"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ব্রডকাস্ট করা হচ্ছে"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> সম্প্রচার বন্ধ করবেন?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"আপনি <xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্রচার করলে বা আউটপুট পরিবর্তন করলে, আপনার বর্তমান সম্প্রচার বন্ধ হয়ে যাবে"</string>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 59061c223580..5c3c66c29461 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"বন্ধ আছে"</item>
<item msgid="578444932039713369">"চালু আছে"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"উপলভ্য নেই"</item>
<item msgid="8707481475312432575">"বন্ধ আছে"</item>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f79e2d915451..db102a0ed68b 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte još vidžeta"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pritisnite i zadržite da prilagodite vidžete"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodite vidžete"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Uredite vidžet"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Uklanjanje"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajte vidžet"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvono"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriranje"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Isključi zvuk"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Emitiraj"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno zbog isključenog zvona"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da postavite vibraciju."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dodirnite da isključite zvuk."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Upravljanje bukom"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meni napajanja"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključani ekran"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Pogledajte korake za zaštitu"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte korake za zaštitu"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Iskopčajte uređaj"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Pisanje bilješki"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pisanje bilješki, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Dijeljenje zvuka"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, trenutno emitiranje će se zaustaviti"</string>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index 32051ef19743..217d99975d3f 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Isključeno"</item>
<item msgid="578444932039713369">"Uključeno"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nedostupno"</item>
<item msgid="8707481475312432575">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 583275f366b3..c5287344c7c0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Afegeix més widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén premut per personalitzar els widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalitza els widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Edita el widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Suprimeix"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Afegeix un widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Fes sonar"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibra"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silencia"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Emet"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible perquè el so està silenciat"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca per activar el so."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca per activar la vibració. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toca per activar la vibració."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca per silenciar."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de soroll"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"deixar de silenciar"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú d\'engegada"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pàgina <xliff:g id="ID_1">%1$d</xliff:g> (<xliff:g id="ID_2">%2$d</xliff:g> en total)"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueig"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Mostra els passos de manteniment"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Mostra els passos de manteniment"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconnecta el dispositiu"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Presa de notes"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Presa de notes, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"S\'està compartint l\'àudio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"S\'està emetent"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vols deixar d\'emetre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emets <xliff:g id="SWITCHAPP">%1$s</xliff:g> o canvies la sortida, l\'emissió actual s\'aturarà"</string>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index e99926c57324..c1ac5a356f05 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Desactivat"</item>
<item msgid="578444932039713369">"Activat"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"No disponible"</item>
<item msgid="8707481475312432575">"Desactivat"</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 6cdb3d8af2a1..f29dabb388d8 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Přidat další widgety"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dlouhým stisknutím můžete přizpůsobit widgety"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Přizpůsobit widgety"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Upravit widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Odstranit"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Přidat widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Vyzvánění"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrace"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ztlumení"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Odesílání"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, protože vyzvánění je ztlumené"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnete zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujete režim vibrací. Služby přístupnosti mohou být ztlumeny."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Klepnutím nastavíte vibrace."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Klepnutím vypnete zvuk."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Omezení hluku"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Klepnutím změníte režim vyzvánění"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnout zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnout zvuk"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Nabídka vypínače"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stránka <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Obrazovka uzamčení"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Zobrazit pokyny, co dělat"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobrazit pokyny, co dělat"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Odpojte zařízení"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g> <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Psaní poznámek"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Psaní poznámek, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Sdílení zvuku"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysílání"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zastavit vysílání v aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Pokud budete vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g> nebo změníte výstup, aktuální vysílání se zastaví"</string>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index 6359f94112bb..0a4d4d0cfeef 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Vypnuto"</item>
<item msgid="578444932039713369">"Zapnuto"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nedostupné"</item>
<item msgid="8707481475312432575">"Vypnuto"</item>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 79b01a84b3bb..ec899f2f2c20 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Tilføj flere widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Hold fingeren nede for at tilpasse widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tilpas widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Rediger widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tilføj widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibration"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Slå lyden fra"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ikke muligt, da ringetonen er slået fra"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryk for at slå lyden til."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryk for at konfigurere til at vibrere. Tilgængelighedstjenester kan blive deaktiveret."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tryk for at aktivere vibration."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tryk for at slå lyden fra."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Støjstyring"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryk for at ændre ringetilstand"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"slå lyden fra"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå lyden til"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu for afbryderknappen"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Side <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Låseskærm"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Se håndteringsvejledning"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Se håndteringsvejledning"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Træk stikket ud af din enhed"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Notetagning"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notetagning, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Deler lyd"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Udsender"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop udsendelsen <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du udsender <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller skifter output, stopper din aktuelle udsendelse"</string>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 1daed4cd6864..2391753f8c0b 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Fra"</item>
<item msgid="578444932039713369">"Til"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Ikke tilgængelig"</item>
<item msgid="8707481475312432575">"Fra"</item>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 43f799bd1b68..f7e74c906960 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Weitere Widgets hinzufügen"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Lange drücken, um Widgets anzupassen"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widgets anpassen"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Widget bearbeiten"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Entfernen"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget hinzufügen"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Klingeln lassen"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrieren"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Stummschalten"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Stream"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nicht verfügbar, da Klingelton stumm"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Zum Aufheben der Stummschaltung tippen."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Zum Aktivieren der Vibration tippen."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Zum Stummschalten tippen."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Geräuschunterdrückung"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zum Ändern des Klingeltonmodus tippen"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Stummschalten"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"Aufheben der Stummschaltung"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Ein-/Aus-Menü"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Seite <xliff:g id="ID_1">%1$d</xliff:g> von <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Sperrbildschirm"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Schritte zur Abkühlung des Geräts ansehen"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Schritte zur Abkühlung des Geräts ansehen"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Gerät vom Stromnetz trennen"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Notizen"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notizen, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audio wird geteilt"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Übertragung läuft"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 9a087472bd03..3aae04bbc931 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Aus"</item>
<item msgid="578444932039713369">"An"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nicht verfügbar"</item>
<item msgid="8707481475312432575">"Aus"</item>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 43ab777aa1de..6b341fdf1d93 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Προσθήκη περισσότερων γραφικών στοιχείων"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Παρατεταμένο πάτημα για προσαρμογή γραφ. στοιχείων"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Προσαρμογή γραφικών στοιχείων"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Επεξεργασία γραφικού στοιχείου"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Κατάργηση"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Προσθήκη γραφικού στοιχείου"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Κουδούνισμα"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Δόνηση"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Σίγαση"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Μετάδοση"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Μη διαθέσιμο λόγω σίγασης ήχου κλήσης"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Πατήστε για κατάργηση σίγασης."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Πατήστε για ενεργοποιήσετε τη δόνηση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Πατήστε για να ενεργοποιήσετε τη δόνηση."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Πατήστε για σίγαση."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Έλεγχος θορύβου"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Πατήστε για να αλλάξετε τη λειτουργία ειδοποίησης ήχου"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Μενού λειτουργίας"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Σελίδα <xliff:g id="ID_1">%1$d</xliff:g> από <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Οθόνη κλειδώματος"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Δείτε βήματα αντιμετώπισης."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Δείτε βήματα αντιμετώπισης."</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Αποσυνδέστε τη συσκευή"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Δημιουργία σημειώσεων"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Δημιουργία σημειώσεων, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Κοινοποίηση ήχου"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Μετάδοση"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Διακοπή μετάδοσης με την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Εάν κάνετε μετάδοση με την εφαρμογή <xliff:g id="SWITCHAPP">%1$s</xliff:g> ή αλλάξετε την έξοδο, η τρέχουσα μετάδοση θα σταματήσει"</string>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 4d94515ac130..035f1171d3b4 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Ανενεργό"</item>
<item msgid="578444932039713369">"Ενεργό"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Μη διαθέσιμο"</item>
<item msgid="8707481475312432575">"Ανενεργό"</item>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 9d2742a2b9fe..021f7db6c2b6 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
<string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrate"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Mute"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Noise control"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
+ <string name="finder_active" msgid="7907846989716941952">"You can locate this phone with Find My Device even when powered off"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"Shutting down…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Unplug your device"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Sharing audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 0cf28684aa48..2576b6080638 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Off"</item>
<item msgid="578444932039713369">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Unavailable"</item>
<item msgid="8707481475312432575">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index f77f66028397..14cb7a0e0f36 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customize widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customize widgets"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
<string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrate"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Mute"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Noise Control"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
+ <string name="finder_active" msgid="7907846989716941952">"You can locate this phone with Find My Device even when powered off"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"Shutting down…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Unplug your device"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Sharing audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index 0cf28684aa48..2576b6080638 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Off"</item>
<item msgid="578444932039713369">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Unavailable"</item>
<item msgid="8707481475312432575">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 9d2742a2b9fe..021f7db6c2b6 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
<string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrate"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Mute"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Noise control"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
+ <string name="finder_active" msgid="7907846989716941952">"You can locate this phone with Find My Device even when powered off"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"Shutting down…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Unplug your device"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Sharing audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 0cf28684aa48..2576b6080638 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Off"</item>
<item msgid="578444932039713369">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Unavailable"</item>
<item msgid="8707481475312432575">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 9d2742a2b9fe..021f7db6c2b6 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Customise widgets"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"App icon for disabled widget"</string>
<string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrate"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Mute"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Noise control"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
+ <string name="finder_active" msgid="7907846989716941952">"You can locate this phone with Find My Device even when powered off"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"Shutting down…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"See care steps"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"See care steps"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Unplug your device"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Sharing audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 0cf28684aa48..2576b6080638 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Off"</item>
<item msgid="578444932039713369">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Unavailable"</item>
<item msgid="8707481475312432575">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 6f5c13362405..af690e475b75 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎Add more widgets‎‏‎‎‏‎"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎Long press to customize widgets‎‏‎‎‏‎"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎Customize widgets‎‏‎‎‏‎"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎App icon for disabled widget‎‏‎‎‏‎"</string>
<string name="edit_widget" msgid="9030848101135393954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎Edit widget‎‏‎‎‏‎"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎Remove‎‏‎‎‏‎"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎Add widget‎‏‎‎‏‎"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‎Ring‎‏‎‎‏‎"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‎Vibrate‎‏‎‎‏‎"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎Mute‎‏‎‎‏‎"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎Cast‎‏‎‎‏‎"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎Unavailable because ring is muted‎‏‎‎‏‎"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎%1$s. Tap to unmute.‎‏‎‎‏‎"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎%1$s. Tap to set to vibrate. Accessibility services may be muted.‎‏‎‎‏‎"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎%1$s. Tap to mute. Accessibility services may be muted.‎‏‎‎‏‎"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎%1$s. Tap to set to vibrate.‎‏‎‎‏‎"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎%1$s. Tap to mute.‎‏‎‎‏‎"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‎Noise Control‎‏‎‎‏‎"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‎‎Tap to change ringer mode‎‏‎‎‏‎"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎mute‎‏‎‎‏‎"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎unmute‎‏‎‎‏‎"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‎Power menu‎‏‎‎‏‎"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎Page ‎‏‎‎‏‏‎<xliff:g id="ID_1">%1$d</xliff:g>‎‏‎‎‏‏‏‎ of ‎‏‎‎‏‏‎<xliff:g id="ID_2">%2$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎Lock screen‎‏‎‎‏‎"</string>
+ <string name="finder_active" msgid="7907846989716941952">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‎You can locate this phone with Find My Device even when powered off‎‏‎‎‏‎"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎Shutting down…‎‏‎‎‏‎"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎See care steps‎‏‎‎‏‎"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎See care steps‎‏‎‎‏‎"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‎Unplug your device‎‏‎‎‏‎"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="TEMPERATURE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="note_task_button_label" msgid="230135078402003532">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎Note-taking‎‏‎‎‏‎"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‎‎‏‎Note-taking, ‎‏‎‎‏‏‎<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎Sharing audio‎‏‎‎‏‎"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‎Broadcasting‎‏‎‎‏‎"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎Stop broadcasting ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎If you broadcast ‎‏‎‎‏‏‎<xliff:g id="SWITCHAPP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ or change the output, your current broadcast will stop‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
index b9c8e5fcdd6a..42daf8a6b23d 100644
--- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‎‏‎Off‎‏‎‎‏‎"</item>
<item msgid="578444932039713369">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎On‎‏‎‎‏‎"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎Unavailable‎‏‎‎‏‎"</item>
<item msgid="8707481475312432575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‎Off‎‏‎‎‏‎"</item>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8c64a0767cfd..1798e9aa4b2d 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Agregar más widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén presionado para personalizar los widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícono de la app de widget inhabilitado"</string>
<string name="edit_widget" msgid="9030848101135393954">"Modificar widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Agregar widget"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Timbre"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenciar"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Transmisión"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible por timbre silenciado"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Presiona para dejar de silenciar."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Presiona para establecer el modo vibración. Es posible que los servicios de accesibilidad estén silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Presiona para establecer el modo vibración."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Presiona para silenciar."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de ruido"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Presiona para cambiar el modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú de encendido"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
+ <string name="finder_active" msgid="7907846989716941952">"Puedes ubicar este teléfono con Encontrar mi dispositivo, incluso si está apagado"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"Apagando…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver pasos de mantenimiento"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantenimiento"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Desenchufa el dispositivo"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Tomar notas"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Tomar notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Compartiendo audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitiendo"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Quieres dejar de transmitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si transmites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu transmisión actual se detendrá"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index bb3983b0bb09..09abc543dbcf 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Desactivado"</item>
<item msgid="578444932039713369">"Activado"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"No disponible"</item>
<item msgid="8707481475312432575">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 692bb26fb190..73c913fe9a6d 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Añade más widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén pulsado para personalizar los widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Añadir widget"</string>
@@ -530,7 +532,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tu padre o madre gestionan este dispositivo y pueden ver y controlar cierta información, como las aplicaciones que utilizas, tu ubicación y tu tiempo de pantalla."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado por TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirrobo\nDispositivo bloqueado; demasiados intentos"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirrobo\nDispositivo bloqueado por nº de intentos"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ajustes de sonido"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Subtitular automáticamente"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Hacer sonar"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenciar"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Enviar"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible (el tono está silenciado)"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar el sonido."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para poner el dispositivo en vibración. Los servicios de accesibilidad pueden silenciarse."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toca para activar la vibración."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca para silenciar."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de ruido"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar el modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú de encendido"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver pasos de mantenimiento"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantenimiento"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Desenchufa tu dispositivo"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Toma de notas"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Toma de notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Compartiendo audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiendo"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Dejar de emitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu emisión actual se detendrá"</string>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index 66c7ee5efe6c..83b4627897da 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Desactivado"</item>
<item msgid="578444932039713369">"Activado"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"No disponible"</item>
<item msgid="8707481475312432575">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 070f200f2d74..6fa704490367 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lisage rohkem vidinaid"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Vajutage pikalt vidinate kohandamiseks"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Kohanda vidinaid"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Muuda vidinat"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Eemalda"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisa vidin"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Helisemine"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibreerimine"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Vaigistatud"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Ülekandmine"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Pole saadaval, kuna helin on vaigistatud"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Puudutage vaigistuse tühistamiseks."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Puudutage vibreerimise määramiseks."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Puudutage vaigistamiseks."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Mürasummutus"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Puudutage telefonihelina režiimi muutmiseks"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vaigistamine"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vaigistuse tühistamine"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Toitemenüü"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Leht <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lukustuskuva"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Vaadake hooldusjuhiseid"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vaadake hooldusjuhiseid"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Eemaldage seade"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Märkmete tegemine"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Märkmete tegemine <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Jagab heli"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Edastamine"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Kas peatada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> ülekandmine?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kui kannate rakendust <xliff:g id="SWITCHAPP">%1$s</xliff:g> üle või muudate väljundit, peatatakse teie praegune ülekanne"</string>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 6a9edbbe812a..4f0551d7af74 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Väljas"</item>
<item msgid="578444932039713369">"Sees"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Pole saadaval"</item>
<item msgid="8707481475312432575">"Väljas"</item>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 36eac0ce42b9..564fbb395aeb 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -346,7 +346,7 @@
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string>
- <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabagailua"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string>
<string-array name="qs_record_issue_types">
<item msgid="2947988124014085798">"Errendimendua"</item>
<item msgid="1627504621139124393">"Erabiltzaile-interfazea"</item>
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Gehitu widget gehiago"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widgetak pertsonalizatzeko, sakatu luze"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Pertsonalizatu widgetak"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Editatu widgeta"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Kendu"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Gehitu widget bat"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Jo tonua"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Dardara"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ez jo tonua"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Igorri"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ez dago erabilgarri, tonua desaktibatuta dagoelako"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sakatu audioa aktibatzeko."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Sakatu hau dardara ezartzeko."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Sakatu hau audioa desaktibatzeko."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Zarata-murrizketa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Sakatu tonu-jotzailearen modua aldatzeko"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desaktibatu audioa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktibatu audioa"</string>
@@ -725,7 +730,7 @@
<string name="group_system_full_screenshot" msgid="5742204844232667785">"Atera pantaila-argazki bat"</string>
<string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Erakutsi lasterbideak"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Egin atzera"</string>
- <string name="group_system_access_home_screen" msgid="4130366993484706483">"Joan hasierako pantailara"</string>
+ <string name="group_system_access_home_screen" msgid="4130366993484706483">"Joan orri nagusira"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Ikusi azkenaldiko aplikazioak"</string>
<string name="group_system_cycle_forward" msgid="5478663965957647805">"Ikusi azken aplikazioak banan-banan (aurrerantz)"</string>
<string name="group_system_cycle_back" msgid="8194102916946802902">"Ikusi azken aplikazioak banan-banan (atzerantz)"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Itzaltzeko menua"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g> orria"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantaila blokeatua"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ikusi zaintzeko urratsak"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ikusi zaintzeko urratsak"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Deskonektatu gailua"</string>
@@ -1085,7 +1094,7 @@
<string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string>
<string name="basic_status" msgid="2315371112182658176">"Elkarrizketa irekia"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"Elkarrizketa-widgetak"</string>
- <string name="select_conversation_text" msgid="3376048251434956013">"Sakatu elkarrizketa bat hasierako pantailan gehitzeko"</string>
+ <string name="select_conversation_text" msgid="3376048251434956013">"Sakatu elkarrizketa bat orri nagusian gehitzeko"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"Azken elkarrizketak agertuko dira hemen"</string>
<string name="priority_conversations" msgid="3967482288896653039">"Lehentasunezko elkarrizketak"</string>
<string name="recent_conversations" msgid="8531874684782574622">"Azken elkarrizketak"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Oharrak idaztea"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Oharrak idaztea, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audioa partekatzen"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Igortzen"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren audioa igortzeari utzi nahi diozu?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa igortzen baduzu, edo audio-irteera aldatzen baduzu, une hartako igorpena eten egingo da"</string>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index d023076ca2bf..accecacbe8dd 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Desaktibatuta"</item>
<item msgid="578444932039713369">"Aktibatuta"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Ez dago erabilgarri"</item>
<item msgid="8707481475312432575">"Desaktibatuta"</item>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index b3a5e36327db..95f17b0d1e56 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"افزودن ابزارک‌های بیشتر"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"برای سفارشی‌سازی ابزارک‌ها، فشار طولانی دهید"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"سفارشی‌سازی ابزارک‌ها"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ویرایش ابزارک"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"برداشتن"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"افزودن ابزارک"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"زنگ زدن"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"لرزش"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"صامت"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"ارسال محتوا"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"دردسترس نیست، چون زنگ بی‌صدا شده است"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏%1$s. برای باصدا کردن ضربه بزنید."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏%1$s. برای تنظیم روی لرزش ضربه بزنید. ممکن است سرویس‌های دسترس‌پذیری بی‌صدا شوند."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏%1$s. برای صامت کردن ضربه بزنید. ممکن است سرویس‌های دسترس‌پذیری صامت شود."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"‏%1$s. برای تنظیم روی لرزش، ضربه بزنید."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"‏%1$s. برای صامت کردن ضربه بزنید."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"کنترل صدای محیط"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"برای تغییر حالت زنگ، ضربه بزنید"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"منوی روشن/خاموش"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"صفحه <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"صفحه قفل"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"دیدن اقدامات محافظتی"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"دیدن اقدامات محافظتی"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"دستگاه را جدا کنید"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"یادداشت‌برداری"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"یادداشت‌برداری، <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"هم‌رسانی صدا"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"همه‌فرستی"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"همه‌فرستی <xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شود؟"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همه‌فرستی کنید یا خروجی را تغییر دهید، همه‌فرستی کنونی متوقف خواهد شد"</string>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index b341e9e64c7b..01a549ed70b3 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"خاموش"</item>
<item msgid="578444932039713369">"روشن"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"دردسترس نیست"</item>
<item msgid="8707481475312432575">"خاموش"</item>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 47e7f2d7dd2d..ab022dd80277 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lisää widgetejä"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Yksilöi widgetit pitkällä painalluksella"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Muokkaa widgettejä"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Muokkaa widgetiä"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Poista"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisää widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Soittoääni"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Värinä"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Äänetön"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Striimaa"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ei käytettävissä, soittoääni mykistetty"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Poista mykistys koskettamalla."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Siirry värinätilaan koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Siirry värinätilaan napauttamalla."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Mykistä napauttamalla."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Melunvaimennus"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Vaihda soittoäänen tilaa napauttamalla"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mykistä"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"poista mykistys"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Virtavalikko"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sivu <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lukitusnäyttö"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Katso huoltovaiheet"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Katso huoltovaiheet"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Irrota laite"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Muistiinpanojen tekeminen"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Muistiinpanot, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audiota jaetaan"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Lähettää"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Lopetetaanko <xliff:g id="APP_NAME">%1$s</xliff:g>-sovelluksen lähettäminen?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jos lähetät <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta tai muutat ulostuloa, nykyinen lähetyksesi loppuu"</string>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index bbd64fdb87da..f7a8ec94f1bd 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Poissa päältä"</item>
<item msgid="578444932039713369">"Päällä"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Ei saatavilla"</item>
<item msgid="8707481475312432575">"Poissa päältä"</item>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 1bb8147b9cdf..1186c81c1a10 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ajouter plus de widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Maintenez le doigt pour personnaliser les widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personnaliser les widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Modifier le widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Retirer"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonnerie"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibration"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Sonnerie désactivée"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Diffuser"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Inaccessible : sonnerie en sourdine"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Touchez pour réactiver le son."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Touchez pour activer les vibrations."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Touchez pour couper le son."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Contrôle du bruit"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Touchez pour modifier le mode de sonnerie"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu de l\'interrupteur"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Écran de verrouillage"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Débranchez votre appareil"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Prise de note"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Prise de note, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audio partagé"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion en cours…"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou changez la sortie, votre diffusion actuelle s\'arrêtera"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index b96984173d37..7b9708ef03f4 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Désactivé"</item>
<item msgid="578444932039713369">"Activé"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Non disponible"</item>
<item msgid="8707481475312432575">"Désactivé"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 84160af98955..a02c9f7f9e6f 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -385,8 +385,8 @@
<string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'écran Récents"</string>
- <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez dérangé par aucun son ni aucune vibration, hormis ceux des alarmes, des rappels, des événements et des appels des contacts de votre choix. Le son continuera de fonctionner notamment pour la musique, les vidéos et les jeux."</string>
- <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez dérangé par aucun son ni aucune vibration, hormis ceux des alarmes. Le son continuera de fonctionner notamment pour la musique, les vidéos et les jeux."</string>
+ <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
+ <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
<string name="zen_priority_customize_button" msgid="4119213187257195047">"Personnaliser"</string>
<string name="zen_silence_introduction_voice" msgid="853573681302712348">"Cette option permet de bloquer TOUS les sons et les vibrations, y compris pour les alarmes, la musique, les vidéos et les jeux. Vous pourrez encore passer des appels téléphoniques."</string>
<string name="zen_silence_introduction" msgid="6117517737057344014">"Cette option permet de bloquer TOUS les sons et les vibrations, y compris pour les alarmes, la musique, les vidéos et les jeux."</string>
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ajouter des widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Appuyez de manière prolongée pour personnaliser les widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personnaliser les widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Modifier le widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Supprimer"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonnerie"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibreur"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Couper le son"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Caster"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponible, car la sonnerie est coupée"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Appuyez pour ne plus ignorer."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Appuyez pour mettre en mode vibreur. Vous pouvez ignorer les services d\'accessibilité."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Appuyez pour mettre en mode vibreur."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Appuyez pour ignorer."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Contrôle du bruit"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Appuyez pour changer le mode de la sonnerie"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"couper le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu Marche/Arrêt"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Écran de verrouillage"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Afficher les étapes d\'entretien"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Débrancher votre appareil"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Prise de notes"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Prise de notes, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audio partagé"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion…"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou que vous modifiez le résultat, votre annonce actuelle s\'arrêtera"</string>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 34440a0d299b..af1d09d3db3d 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Désactivé"</item>
<item msgid="578444932039713369">"Activé"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Indisponible"</item>
<item msgid="8707481475312432575">"Désactivée"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index b78199a149cf..e758af880582 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Engadir máis widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pulsación longa para personalizar os widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engadir widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Facer soar"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenciar"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Emitir"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non dispoñible (o son está silenciado)"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar o son."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toca para establecer a vibración."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca para silenciar."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de ruído"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar o modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activar o son"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú de acendido"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Páxina <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver pasos de mantemento"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantemento"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconectar o dispositivo"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Toma de notas"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Toma de notas (<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>)"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Compartindo audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Difusión"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Queres deixar de emitir contido a través de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se emites contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou cambias de saída, a emisión en curso deterase"</string>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index b03f31171e5c..a963decd28e9 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Non"</item>
<item msgid="578444932039713369">"Si"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Non dispoñible"</item>
<item msgid="8707481475312432575">"Non"</item>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index d84b1f553f8b..6d1d8df1bc27 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"વધુ વિજેટ ઉમેરો"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"વિજેટ કસ્ટમાઇઝ કરવા માટે થોડીવાર દબાવી રાખો"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"વિજેટ કસ્ટમાઇઝ કરો"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"વિજેટમાં ફેરફાર કરો"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"કાઢી નાખો"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"વિજેટ ઉમેરો"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"રિંગ કરો"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"વાઇબ્રેટ"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"મ્યૂટ કરો"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"કાસ્ટ કરો"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"રિંગ મ્યૂટ કરી હોવાના કારણે અનુપલબ્ધ છે"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. અનમ્યૂટ કરવા માટે ટૅપ કરો."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. વાઇબ્રેટ પર સેટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. કંપન પર સેટ કરવા માટે ટૅપ કરો."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"અવાજનું નિયંત્રણ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"મ્યૂટ કરો"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"અનમ્યૂટ કરો"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"પાવર મેનૂ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> માંથી <xliff:g id="ID_1">%1$d</xliff:g> પૃષ્ઠ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"લૉક સ્ક્રીન"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"સારસંભાળના પગલાં જુઓ"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"સારસંભાળના પગલાં જુઓ"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"તમારા ડિવાઇસને અનપ્લગ કરો"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"નોંધ લેવી"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"નોંધ લેવી, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ઑડિયો શેર કરી રહ્યાં છીએ"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"બ્રૉડકાસ્ટ કરી રહ્યાં છે"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> બ્રોડકાસ્ટ કરવાનું રોકીએ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"જો તમે <xliff:g id="SWITCHAPP">%1$s</xliff:g> બ્રોડકાસ્ટ કરો અથવા આઉટપુટ બદલો, તો તમારું હાલનું બ્રોડકાસ્ટ બંધ થઈ જશે"</string>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index 5d1ad6ff0514..580ec104dfb4 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"બંધ છે"</item>
<item msgid="578444932039713369">"ચાલુ છે"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"ઉપલબ્ધ નથી"</item>
<item msgid="8707481475312432575">"બંધ છે"</item>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 8d1ac11b933f..2035429a00b9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ज़्यादा विजेट जोड़ें"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट पसंद के मुताबिक बनाने के लिए उसे दबाकर रखें"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेट अपनी पसंद के मुताबिक बनाएं"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"विजेट में बदलाव करें"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"हटाएं"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट जोड़ें"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"आवाज़ चालू है"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"वाइब्रेशन"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"आवाज़ बंद है"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"कास्ट करें"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"रिंग म्यूट होने से आवाज़ नहीं सुनाई दी"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करने के लिए टैप करें."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. कंपन पर सेट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. कंपन (वाइब्रेशन) पर सेट करने के लिए छूएं."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. म्यूट करने के लिए टैप करें."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"शोर को कंट्रोल करने की सुविधा"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलने के लिए टैप करें"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करें"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्यूट करें"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"पावर मेन्यू"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"पेज <xliff:g id="ID_2">%2$d</xliff:g> में से <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"लॉक स्‍क्रीन"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"डिवाइस के रखरखाव के तरीके देखें"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"डिवाइस के रखरखाव के तरीके देखें"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"डिवाइस को अनप्लग करें"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"नोट बनाएं"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"नोट लेने के लिए, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ऑडियो शेयर किया जा रहा है"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट ऐप्लिकेशन"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट शुरू करने पर या आउटपुट बदलने पर, आपका मौजूदा ब्रॉडकास्ट बंद हो जाएगा"</string>
@@ -1236,9 +1246,9 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग में जाकर, नोट लेने की सुविधा देने वाले ऐप्लिकेशन को डिफ़ॉल्ट के तौर पर सेट करें"</string>
<string name="install_app" msgid="5066668100199613936">"ऐप्लिकेशन इंस्टॉल करें"</string>
<string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"जारी रखने के लिए, ऊपर की ओर स्वाइप करें"</string>
- <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"क्या आपको किसी बाहरी डिवाइस पर डिसप्ले करना है?"</string>
+ <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"क्या आपको किसी बाहरी डिवाइस पर स्क्रीन शेयर करनी है?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"आपके फ़ोन के इनर डिसप्ले की स्क्रीन शेयर की जाएगी. फ़्रंट डिसप्ले को बंद कर दिया जाएगा."</string>
- <string name="mirror_display" msgid="2515262008898122928">"डिसप्ले करें"</string>
+ <string name="mirror_display" msgid="2515262008898122928">"स्क्रीन शेयर करें"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"खारिज करें"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"डिसप्ले कनेक्ट किया गया"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"माइक्रोफ़ोन और कैमरा"</string>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index cd29fb92063f..3fd0b30ea4a0 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"बंद है"</item>
<item msgid="578444932039713369">"चालू है"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"उपलब्ध नहीं है"</item>
<item msgid="8707481475312432575">"बंद है"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d4e460ddf9ca..d50a95121148 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodavanje još widgeta"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dugo pritisnite za prilagodbu widgeta"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodi widgete"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Uredi widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvonjenje"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriranje"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Zvuk je isključen"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Emitiraj"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvono utišano"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste postavili na vibraciju. Usluge pristupačnosti možda neće imati zvuk."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da biste postavili na vibraciju."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dodirnite da biste isključili zvuk."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kontrola buke"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promijenili način softvera zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključivanje zvuka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključivanje zvuka"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Izbornik tipke za uključivanje/isključivanje"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključani zaslon"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Pročitajte upute za održavanje"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pročitajte upute za održavanje"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Iskopčajte uređaj"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Pisanje bilježaka"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pisanje bilježaka, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Dijeljenje zvuka"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, vaše će se trenutačno emitiranje zaustaviti"</string>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index 32051ef19743..217d99975d3f 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Isključeno"</item>
<item msgid="578444932039713369">"Uključeno"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nedostupno"</item>
<item msgid="8707481475312432575">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7c62c7a813e8..b09419bd396d 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"További modulok hozzáadása"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nyomja meg hosszan a modulok személyre szabásához"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Modulok személyre szabása"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Modul szerkesztése"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Eltávolítás"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Modul hozzáadása"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Csörgés"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Rezgés"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Néma"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Átküldés"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nem lehetséges, a csörgés le van némítva"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Koppintson a némítás megszüntetéséhez."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Koppintson a rezgés beállításához."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Koppintson a némításhoz."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Zajszabályozás"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Koppintson a csengés módjának módosításához"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"némítás"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"némítás feloldása"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Bekapcsológombhoz tartozó menü"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. oldal, összesen: <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lezárási képernyő"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Olvassa el a kímélő használat lépéseit"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Olvassa el a kímélő használat lépéseit"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Húzza ki az eszközt"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Jegyzetelés"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Jegyzetelés, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Hang megosztása…"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sugárzás"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Leállítja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> közvetítését?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"A(z) <xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése vagy a kimenet módosítása esetén a jelenlegi közvetítés leáll"</string>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 157c552b31a6..fad2cd4e6d4b 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Ki"</item>
<item msgid="578444932039713369">"Be"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nem áll rendelkezésre"</item>
<item msgid="8707481475312432575">"Ki"</item>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 8427fc3b683f..4ea86d0571bb 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ավելացնել վիջեթներ"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Երկար սեղմեք՝ վիջեթները հարմարեցնելու համար"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Հարմարեցնել վիջեթները"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Փոփոխել վիջեթը"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Հեռացնել"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ավելացնել վիջեթ"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Սովորական"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Թրթռոց"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Անձայն"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Հեռարձակում"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Հասանելի չէ, երբ զանգի ձայնն անջատված է"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռումը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s։ Հպեք՝ թրթռոցը միացնելու համար։"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s։ Հպեք՝ ձայնը անջատելու համար։"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Աղմուկի կառավարում"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Հպեք՝ զանգակի ռեժիմը փոխելու համար"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"անջատել ձայնը"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"միացնել ձայնը"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Սնուցման կոճակի ընտրացանկ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Էջ <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Կողպէկրան"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Քայլեր գերտաքացման ահազանգի դեպքում"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Քայլեր գերտաքացման ահազանգի դեպքում"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Անջատեք սարքը"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Նշումների ստեղծում"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Նշումների ստեղծում, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Փոխանցում է ձայնը"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Հեռարձակում"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Կանգնեցնել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի հեռարձակումը"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Եթե հեռարձակեք <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը կամ փոխեք աուդիո ելքը, ձեր ընթացիկ հեռարձակումը կկանգնեցվի։"</string>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index 089716fa51d6..380d9d2d9a95 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Անջատված է"</item>
<item msgid="578444932039713369">"Միացված է"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Հասանելի չէ"</item>
<item msgid="8707481475312432575">"Անջատված է"</item>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a2f05b95026f..188f3fbcd14d 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Tambahkan widget lainnya"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tekan lama untuk menyesuaikan widget"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Sesuaikan widget"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Hapus"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Dering"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Getar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Nonaktifkan"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Transmisi"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia karena volume dering dibisukan"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketuk untuk menyuarakan."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketuk untuk menyetel agar bergetar. Layanan aksesibilitas mungkin dibisukan."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Ketuk untuk menyetel agar bergetar."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ketuk untuk menonaktifkan."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kontrol Bising"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketuk untuk mengubah mode pendering"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Tanpa suara"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktifkan"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu daya"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> dari <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Layar kunci"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Lihat langkah-langkah perawatan"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Lihat langkah-langkah perawatan"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Cabut perangkat"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Pembuatan catatan"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pembuatan catatan, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Berbagi audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika Anda menyiarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau mengubah output, siaran saat ini akan dihentikan"</string>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index 71460a71b414..9be5d0276a24 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Nonaktif"</item>
<item msgid="578444932039713369">"Aktif"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Tidak tersedia"</item>
<item msgid="8707481475312432575">"Nonaktif"</item>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 9c9a29f513fc..47756c213877 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Bæta við fleiri græjum"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Haltu inni til að sérsníða græjur"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Sérsníða græjur"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Breyta græju"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Fjarlægja"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Bæta græju við"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Hringing"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Titringur"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Hljóð af"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Senda út"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ekki í boði þar sem hringing er þögguð"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ýttu til að hætta að þagga."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ýttu til að stilla á titring. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Ýttu til að stilla á titring."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ýttu til að þagga."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Hávaðavörn"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ýta til að skipta um hringjarastillingu"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"þagga"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"hætta að þagga"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Aflrofavalmynd"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Blaðsíða <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lásskjár"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Sjá varúðarskref"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Sjá varúðarskref"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Taktu tækið úr sambandi"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Glósugerð"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Glósugerð, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Deilir hljóði"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Útsending í gangi"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hætta að senda út <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ef þú sendir út <xliff:g id="SWITCHAPP">%1$s</xliff:g> eða skiptir um úttak lýkur yfirstandandi útsendingu"</string>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index 17aaf6c2a877..1ee6e471fad3 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Slökkt"</item>
<item msgid="578444932039713369">"Kveikt"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Ekki í boði"</item>
<item msgid="8707481475312432575">"Slökkt"</item>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index ec58af8fe1af..5bad44c836a7 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -99,7 +99,7 @@
<string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e altre app aperte hanno rilevato questo screenshot."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Aggiungi alla nota"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Registrazione dello schermo"</string>
- <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaboraz. registraz. schermo"</string>
+ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaborazione registrazione…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Iniziare a registrare?"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Quando registri, Android ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
@@ -111,7 +111,7 @@
<string name="screenrecord_mic_label" msgid="2111264835791332350">"Microfono"</string>
<string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Audio del dispositivo e microfono"</string>
<string name="screenrecord_continue" msgid="4055347133700593164">"Inizia"</string>
- <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Registrazione schermo"</string>
+ <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Registrazione schermo in corso…"</string>
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Registrazione schermo e audio"</string>
<string name="screenrecord_taps_label" msgid="1595690528298857649">"Mostra tocchi sullo schermo"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Interrompi"</string>
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Aggiungi altri widget"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Premi a lungo per personalizzare i widget"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizza widget"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Modifica widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Rimuovi"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Aggiungi widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Attiva suoneria"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Attiva vibrazione"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Silenzia"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Trasmissione"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non disponibile con l\'audio disattivato"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tocca per riattivare l\'audio."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tocca per attivare la vibrazione."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tocca per disattivare l\'audio."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Controllo del rumore"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tocca per cambiare la modalità della suoneria"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenzia"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"riattiva l\'audio"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu del tasto di accensione"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> di <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Schermata di blocco"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Leggi le misure da adottare"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Leggi le misure da adottare"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Scollega il dispositivo"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Aggiunta di note"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Aggiunta di note, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Condivisione di audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Trasmissione in corso…"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vuoi interrompere la trasmissione dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambi l\'uscita, la trasmissione attuale viene interrotta"</string>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 7aa09d420f87..28e28aed82b8 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Off"</item>
<item msgid="578444932039713369">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Non disponibile"</item>
<item msgid="8707481475312432575">"Off"</item>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index c9a71c71867b..154de1523bbb 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"הוספת ווידג\'טים"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"לוחצים לחיצה ארוכה כדי להתאים אישית את הווידג\'טים"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"התאמה אישית של ווידג\'טים"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"עריכת הווידג\'ט"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"הסרה"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"הוספת ווידג\'ט"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"צלצול"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"רטט"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"השתקה"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"‏הפעלת Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"לא זמין כי הצלצול מושתק"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏%1$s. יש להקיש כדי לבטל את ההשתקה."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏%1$s. צריך להקיש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏%1$s. יש להקיש כדי להשתיק. ייתכן ששירותי הנגישות יושתקו."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"‏%1$s. יש להקיש כדי להעביר למצב רטט."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"‏%1$s. יש להקיש כדי להשתיק."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"בקרת הרעש"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"תפריט הפעלה"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"דף <xliff:g id="ID_1">%1$d</xliff:g> מתוך <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"מסך נעילה"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"לצפייה בשלבי הטיפול"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"לצפייה בשלבי הטיפול"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ניתוק המכשיר"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"כתיבת הערות"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"כתיבת הערות, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"שיתוף האודיו"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"שידור"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"האם להפסיק לשדר את התוכן מאפליקציית <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"אם משדרים את התוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g> או משנים את הפלט, השידור הנוכחי יפסיק לפעול"</string>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index bd2a6f7d0d2b..bb3eb10fd719 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"כבוי"</item>
<item msgid="578444932039713369">"פועל"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"לא זמין"</item>
<item msgid="8707481475312432575">"כבוי"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index dc33b0ddc15d..227edd3570d0 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ウィジェットの追加"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長押ししてウィジェットをカスタマイズ"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ウィジェットのカスタマイズ"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"無効なウィジェットのアプリアイコン"</string>
<string name="edit_widget" msgid="9030848101135393954">"ウィジェットを編集"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"削除"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ウィジェットを追加"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"着信音"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"バイブレーション"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"ミュート"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"キャスト"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"着信音がミュートされているため利用できません"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。タップしてミュートを解除します。"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。タップしてバイブレーションに設定します。"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。タップしてミュートします。"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ノイズ コントロール"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"電源ボタン メニュー"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ページ <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ロック画面"</string>
+ <string name="finder_active" msgid="7907846989716941952">"「デバイスを探す」を使うと、電源が OFF の状態でもこのスマートフォンの現在地を確認できます"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"シャットダウン中…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"取り扱いに関する手順をご覧ください"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"取り扱いに関する手順をご覧ください"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"デバイスを電源から外します"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>、<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"メモ"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"メモ、<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"音声を共有中"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ブロードキャスト"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> のブロードキャストを停止しますか?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> をブロードキャストしたり、出力を変更したりすると、現在のブロードキャストが停止します。"</string>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index 31158ca59882..ebadf3b385ce 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"OFF"</item>
<item msgid="578444932039713369">"ON"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"使用不可"</item>
<item msgid="8707481475312432575">"OFF"</item>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index d78bc2072734..70eeb334556d 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ვიჯეტების დამატება"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ხანგრძლივად დააჭირეთ ვიჯეტების მოსარგებად"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ვიჯეტების მორგება"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ვიჯეტის რედაქტირება"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ამოშლა"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ვიჯეტის დამატება"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"დარეკვა"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ვიბრაცია"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"დადუმება"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"ტრანსლირება"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ზარის დადუმების გამო ხელმისაწვდომი არაა"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. შეეხეთ დადუმების გასაუქმებლად."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. შეეხეთ დასადუმებლად."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ხმაურის კონტროლი"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"შეეხეთ მრეკავის რეჟიმის შესაცვლელად"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"დადუმება"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"დადუმების მოხსნა"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ჩართვის მენიუ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"გვერდი <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>-დან"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ჩაკეტილი ეკრანი"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"მისაღები ზომების გაცნობა"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"მისაღები ზომების გაცნობა"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"გამოაერᲗეᲗ Თქვენი მოწყობილობა"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"ჩანიშვნა"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ჩანიშვნა, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"აუდიოს გაზიარება"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"იწყებთ მაუწყებლობას"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"გსურთ <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ტრანსლაციის შეჩერება?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-ის ტრანსლაციის შემთხვევაში ან აუდიოს გამოსასვლელის შეცვლისას, მიმდინარე ტრანსლაცია შეჩერდება"</string>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 366030a2231f..07a8a76b8c97 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"გამორთულია"</item>
<item msgid="578444932039713369">"ჩართულია"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"მიუწვდომელია"</item>
<item msgid="8707481475312432575">"გამორთულია"</item>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 407da0b9b8fa..c2525d9aec8f 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Басқа виджеттер қосыңыз."</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерді бейімдеу үшін ұзақ басып тұрыңыз."</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджеттерді реттеу"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Виджетті өзгерту"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Өшіру"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет қосу"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Шылдырлау"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Діріл"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Дыбысын өшіру"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Трансляциялау"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Қолжетімді емес, шылдырлату өшірулі."</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дыбысын қосу үшін түртіңіз."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Діріл режимін орнату үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Діріл режимін орнату үшін түртіңіз."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дыбысын өшіру үшін түртіңіз."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Шуды реттеу"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дыбысын өшіру"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дыбысын қосу"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Қуат мәзірі"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ішінен <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Құлыптаулы экран"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Пайдалану нұсқаулығын қараңыз"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Пайдалану нұсқаулығын қараңыз"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Құрылғыны ажыратыңыз"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Ескертпе жазу"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ескертпе жазу, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Аудио бөлісу"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Таратуда"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын таратуды тоқтатасыз ба?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын таратсаңыз немесе аудио шығысын өзгертсеңіз, қазіргі тарату сеансы тоқтайды."</string>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index b8089e44a5ce..f5b094855618 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Өшірулі"</item>
<item msgid="578444932039713369">"Қосулы"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Қолжетімсіз"</item>
<item msgid="8707481475312432575">"Өшірулі"</item>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index eaca65732975..0d2503359790 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"បញ្ចូលធាតុ​ក្រាហ្វិកច្រើនទៀត"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ចុច​ឱ្យ​យូរ ដើម្បីប្ដូរធាតុ​ក្រាហ្វិកតាមបំណង"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ប្ដូរ​ធាតុ​ក្រាហ្វិកតាម​បំណង"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"កែធាតុ​ក្រាហ្វិក"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ដកចេញ"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"បញ្ចូលធាតុ​ក្រាហ្វិក"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"រោទ៍"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ញ័រ"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"បិទសំឡេង"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"បញ្ជូន"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"មិន​អាច​ប្រើ​បាន​ទេ​ ព្រោះ​សំឡេង​រោទ៍​ត្រូវ​បាន​បិទ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s។ ប៉ះដើម្បីបើកសំឡេង។"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s។ ប៉ះដើម្បីកំណត់ឲ្យញ័រ។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s ។ ចុច​ដើម្បី​កំណត់​ឲ្យ​ញ័រ។"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s ។ ចុច​ដើម្បី​បិទ​សំឡេង។"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ការគ្រប់គ្រង​សំឡេងរំខាន"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ចុច​ដើម្បីប្ដូរ​មុខងារ​រោទ៍"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទ​សំឡេង"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើក​សំឡេង"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ម៉ឺនុយ​ថាមពល"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ទំព័រ <xliff:g id="ID_1">%1$d</xliff:g> នៃ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"អេក្រង់​ចាក់សោ"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"មើលជំហាន​ថែទាំ"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"មើលជំហាន​ថែទាំ"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ដកឧបករណ៍របស់អ្នក"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"ការកត់ត្រា"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ការកត់ត្រា, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"កំពុងចែករំលែកសំឡេង"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ការផ្សាយ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"បញ្ឈប់ការផ្សាយ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ប្រសិនបើអ្នក​ផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ឬប្ដូរឧបករណ៍បញ្ចេញសំឡេង ការផ្សាយបច្ចុប្បន្នរបស់អ្នកនឹង​បញ្ឈប់"</string>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index 8c5c8d1ce088..a2031b070a0c 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"បិទ"</item>
<item msgid="578444932039713369">"បើក"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"មិនមានទេ"</item>
<item msgid="8707481475312432575">"បិទ"</item>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 64179ba03a3a..66b8e72ee2cf 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ಹೆಚ್ಚಿನ ವಿಜೆಟ್‌ಗಳನ್ನು ಸೇರಿಸಿ"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ವಿಜೆಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ವಿಜೆಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ವಿಜೆಟ್ ಅನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ತೆಗೆದುಹಾಕಿ"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"ರಿಂಗ್"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ವೈಬ್ರೇಟ್‌"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"ಮ್ಯೂಟ್"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"ಬಿತ್ತರಿಸಿ"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ರಿಂಗ್ ಮ್ಯೂಟ್ ಆಗಿರುವ ಕಾರಣ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ಅನ್‌ಮ್ಯೂಟ್‌ ಮಾಡುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ಕಂಪನಕ್ಕೆ ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್‌ ಮಾಡಬಹುದು."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್‌ ಮಾಡಬಹುದು."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. ವೈಬ್ರೇಟ್ ಮಾಡಲು ಹೊಂದಿಸುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ಗದ್ದಲ ನಿಯಂತ್ರಣ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ರಿಂಗರ್ ಮೋಡ್ ಬದಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ಪವರ್ ಮೆನು"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="ID_1">%1$d</xliff:g> ಪುಟ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ಲಾಕ್ ಪರದೆ"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ಕಾಳಜಿಯ ಹಂತಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ಕಾಳಜಿಯ ಹಂತಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್‌ಪ್ಲಗ್ ಮಾಡಿ"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"ಟಿಪ್ಪಣಿ ಮಾಡಿಕೊಳ್ಳುವಿಕೆ"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ಟಿಪ್ಪಣಿ ಮಾಡಿಕೊಳ್ಳುವಿಕೆ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ಆಡಿಯೊವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ಪ್ರಸಾರ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಪ್ರಸಾರವನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ನೀವು <xliff:g id="SWITCHAPP">%1$s</xliff:g> ಅನ್ನು ಪ್ರಸಾರ ಮಾಡಿದರೆ ಅಥವಾ ಔಟ್‌ಪುಟ್ ಅನ್ನು ಬದಲಾಯಿಸಿದರೆ, ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪ್ರಸಾರವು ಸ್ಥಗಿತಗೊಳ್ಳುತ್ತದೆ"</string>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 250eb5adc8de..de0fcae601cd 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ಆಫ್"</item>
<item msgid="578444932039713369">"ಆನ್"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"ಲಭ್ಯವಿಲ್ಲ"</item>
<item msgid="8707481475312432575">"ಆಫ್"</item>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 26825e0286e3..028c8cf06eb9 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"더 많은 위젯 추가"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"위젯을 맞춤설정하려면 길게 누르기"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"위젯 맞춤설정"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"위젯 수정"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"삭제"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"위젯 추가"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"벨소리"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"진동"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"음소거"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"전송"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"벨소리가 음소거되어 있으므로 사용할 수 없음"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. 탭하여 음소거를 해제하세요."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. 탭하여 진동으로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. 탭하여 진동으로 설정하세요."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. 탭하여 음소거로 설정하세요."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"소음 제어"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"탭하여 벨소리 장치 모드 변경"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"음소거"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"음소거 해제"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"전원 메뉴"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>페이지 중 <xliff:g id="ID_1">%1$d</xliff:g>페이지"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"잠금 화면"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"해결 방법 확인하기"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"해결 방법 확인하기"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"기기 분리하기"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"메모"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"메모, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"오디오 공유 중"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"방송 중"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> 방송을 중지하시겠습니까?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 앱을 방송하거나 출력을 변경하면 기존 방송이 중단됩니다"</string>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index 7981d285946c..c9b2846ad9e1 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"꺼짐"</item>
<item msgid="578444932039713369">"켜짐"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"이용 불가"</item>
<item msgid="8707481475312432575">"꺼짐"</item>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 79546862454b..0f0055561759 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Көбүрөөк виджеттерди кошуу"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерди ыңгайлаштыруу үчүн кое бербей басып туруңуз"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджеттерди ыңгайлаштыруу"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Виджетти түзөтүү"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Өчүрүү"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет кошуу"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Шыңгыратуу"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Дирилдөө"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Үнсүз"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Тышкы экранга чыгруу"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Үнсүз режимде жеткиликсиз"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Үнүн чыгаруу үчүн таптап коюңуз."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дирилдөөгө коюу үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Дирилдөөгө коюу үчүн басыңыз."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Үнүн өчүрүү үчүн басыңыз."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Ызы-чууну көзөмөлдөө"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Коңгуроо режимин өзгөртүү үчүн басыңыз"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"үнсүз"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"үнүн чыгаруу"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Кубат баскычынын менюсу"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ичинен <xliff:g id="ID_1">%1$d</xliff:g>-бет"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Кулпуланган экран"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Тейлөө кадамдарын көрүңүз"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Тейлөө кадамдарын көрүңүз"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Түзмөктү сууруп коюңуз"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Эскертме жазуу"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Эскертме жазуу (<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>)"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Аудиону бөлүшүү"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Кеңири таратуу"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда кабарлоо токтотулсунбу?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Эгер <xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарласаңыз же аудионун чыгуусун өзгөртсөңүз, учурдагы кабарлоо токтотулат"</string>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 0f277f9de292..bc47e5aea38f 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Өчүк"</item>
<item msgid="578444932039713369">"Күйүк"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Жеткиликсиз"</item>
<item msgid="8707481475312432575">"Өчүк"</item>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 4c1bd4eb0d14..db976555009d 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ເພີ່ມວິດເຈັດເພີ່ມເຕີມ"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ກົດຄ້າງໄວ້ເພື່ອປັບແຕ່ງວິດເຈັດ"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ປັບແຕ່ງວິດເຈັດ"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"ໄອຄອນແອັບສຳລັບວິດເຈັດທີ່ຖືກປິດການນຳໃຊ້"</string>
<string name="edit_widget" msgid="9030848101135393954">"ແກ້ໄຂວິດເຈັດ"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ລຶບອອກ"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ເພີ່ມວິດເຈັດ"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"​ເຕືອນ​ດ້ວຍ​ສຽງ"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ສັ່ນເຕືອນ"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"ປິດ"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"ສົ່ງສັນຍານ"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ບໍ່ມີໃຫ້ໃຊ້ເນື່ອງຈາກມີການປິດສຽງໂທເຂົ້າ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ແຕະເພື່ອເຊົາປິດສຽງ."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ແຕະເພື່ອປິດສຽງ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນເຕືອນ."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. ແຕະເພື່ອປິດສຽງ."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ການຄວບຄຸມສຽງລົບກວນ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ແຕະເພື່ອປ່ຽນໂໝດຣິງເກີ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ປິດສຽງ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ເຊົາປິດສຽງ"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ເມນູເປີດປິດ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ໜ້າຈໍລັອກ"</string>
+ <string name="finder_active" msgid="7907846989716941952">"ທ່ານສາມາດຊອກຫາສະຖານທີ່ຂອງໂທລະສັບເຄື່ອງນີ້ໄດ້ດ້ວຍແອັບຊອກຫາອຸປະກອນຂອງຂ້ອຍເຖິງແມ່ນວ່າຈະປິດເຄື່ອງຢູ່ກໍຕາມ"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"ກຳລັງປິດເຄື່ອງ…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ຖອດອຸປະກອນຂອງທ່ານອອກ"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"ການຈົດບັນທຶກ"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ການຈົດບັນທຶກ <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ກຳລັງແບ່ງປັນສຽງ"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ກຳລັງອອກອາກາດ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ຢຸດການອອກອາກາດ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ຫາກທ່ານອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ຫຼື ປ່ຽນເອົ້າພຸດ, ການອອກອາກາດປັດຈຸບັນຂອງທ່ານຈະຢຸດ"</string>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index d54cf4d24181..75958972cdce 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ປິດ"</item>
<item msgid="578444932039713369">"ເປີດ"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
<item msgid="8707481475312432575">"ປິດ"</item>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 9a261e05f709..9eaab8f6a6f0 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Pridėkite daugiau valdiklių"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Ilgai paspauskite, kad tinkintumėte valdiklius"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tinkinti valdiklius"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Redaguoti valdiklį"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Pašalinti"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pridėti valdiklį"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Skambinti"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibruoti"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Nutildyti"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Perdavimas"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nepasiekiama, nes skambutis nutildytas"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Palieskite, kad įjungtumėte garsą."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Palieskite, kad nustatytumėte vibravimą. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Palieskite, kad nustatytumėte vibravimą."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Palieskite, kad nutildytumėte."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Triukšmo valdymas"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Palieskite, kad pakeistumėte skambučio režimą"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"nutildyti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"įjungti garsą"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Įjungimo meniu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g> psl. iš <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Užrakinimo ekranas"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Žr. priežiūros veiksmus"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Žr. priežiūros veiksmus"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Atjunkite įrenginį"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Užrašų kūrimas"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Užrašų kūrimas, „<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>“"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Bendrinamas garsas"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transliavimas"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Sustabdyti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ transliaciją?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jei transliuosite „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“ arba pakeisite išvestį, dabartinė transliacija bus sustabdyta"</string>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index e66f590364dd..94343bac9ff8 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Išjungta"</item>
<item msgid="578444932039713369">"Įjungta"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nepasiekiama"</item>
<item msgid="8707481475312432575">"Išjungta"</item>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a9a1a3c366a4..f69afb93a854 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Pievienot citus logrīkus"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nospiediet un turiet, lai pielāgotu logrīkus."</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Pielāgot logrīkus"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Rediģēt logrīku"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Noņemt"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pievienot logrīku"</string>
@@ -573,11 +575,16 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvanīt"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrēt"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Izslēgt skaņu"</string>
+ <!-- no translation found for media_device_cast (4786241789687569892) -->
+ <skip />
+ <!-- no translation found for stream_notification_unavailable (4313854556205836435) -->
+ <skip />
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Pieskarieties, lai ieslēgtu skaņu."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Pieskarieties, lai iestatītu uz vibrozvanu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Pieskarieties, lai izslēgtu skaņu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Pieskarieties, lai iestatītu vibrozvanu."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Pieskarieties, lai izslēgtu skaņu."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Trokšņu kontrole"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izslēgt skaņu"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ieslēgt skaņu"</string>
@@ -832,6 +839,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Barošanas izvēlne"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. lpp. no <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Bloķēšanas ekrāns"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Skatīt apkopes norādījumus"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Skatīt apkopes norādījumus"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Atvienojiet savu ierīci"</string>
@@ -1186,6 +1197,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Piezīmju pierakstīšana"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Piezīmju pierakstīšana, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Notiek audio kopīgošana"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Notiek apraidīšana"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vai apturēt lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> apraidīšanu?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ja sāksiet lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraidīšanu vai mainīsiet izvadi, pašreizējā apraide tiks apturēta"</string>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index d32efec81a99..d8b24676ea27 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Izslēgta"</item>
<item msgid="578444932039713369">"Ieslēgta"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nav pieejama"</item>
<item msgid="8707481475312432575">"Izslēgta"</item>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 20a93f7df6cf..6ea4d1f1d9ab 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додајте повеќе виџети"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Притиснете долго за да ги приспособите виџетите"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Приспособете ги виџетите"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Изменување виџети"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Отстранува"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додајте виџет"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ѕвони"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрации"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Исклучи звук"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Емитување"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недостапно бидејќи ѕвонењето е исклучено"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Допрете за да вклучите звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Допрете за да се постави на вибрации."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Допрете за да се исклучи звукот."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контрола на бучавата"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Допрете за да го промените режимот на ѕвончето"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"исклучен звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"вклучен звук"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Мени на копчето за вклучување"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заклучен екран"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Прикажи ги чекорите за грижа за уредот"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Прикажи ги чекорите за грижа за уредот"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Исклучете го уредот од кабел"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Фаќање белешки"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Фаќање белешки, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Се споделува аудио"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитување"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се прекине емитувањето на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитувате на <xliff:g id="SWITCHAPP">%1$s</xliff:g> или го промените излезот, тековното емитување ќе запре"</string>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 0a42d7cbe2df..8b0faf7d966e 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Исклучено"</item>
<item msgid="578444932039713369">"Вклучено"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Недостапно"</item>
<item msgid="8707481475312432575">"Исклучено"</item>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index fc2bc47b64dc..82d257a473b2 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"കൂടുതൽ വിജറ്റുകൾ ചേർക്കുക"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കാൻ ദീർഘനേരം അമർത്തുക"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"പ്രവർത്തനരഹിതമാക്കിയ വിജറ്റിനുള്ള ആപ്പ് ഐക്കൺ"</string>
<string name="edit_widget" msgid="9030848101135393954">"വിജറ്റ് എഡിറ്റ് ചെയ്യുക"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"നീക്കം ചെയ്യുക"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"വിജറ്റ് ചേർക്കുക"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"റിംഗ് ചെയ്യുക"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"മ്യൂട്ട് ചെയ്യുക"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"കാസ്റ്റ് ചെയ്യുക"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"റിംഗ് മ്യൂട്ട് ചെയ്തതിനാൽ ലഭ്യമല്ല"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. അൺമ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"നോയ്‌സ് നിയന്ത്രണം"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"റിംഗർ മോഡ് മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"മ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"അൺമ്യൂട്ട് ചെയ്യുക"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"പവർ മെനു"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"പേജ് <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ലോക്ക് സ്‌ക്രീൻ"</string>
+ <string name="finder_active" msgid="7907846989716941952">"ഓഫായിരിക്കുമ്പോഴും Find My Device ഉപയോഗിച്ച് നിങ്ങൾക്ക് ഈ ഫോൺ കണ്ടെത്താനാകും"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"ഷട്ട്‌ഡൗൺ ചെയ്യുന്നു…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"പരിപാലന നിർദ്ദേശങ്ങൾ കാണുക"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"പരിപാലന നിർദ്ദേശങ്ങൾ കാണുക"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ഉപകരണം അൺപ്ലഗ് ചെയ്യുക"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"കുറിപ്പ് രേഖപ്പെടുത്തൽ"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"കുറിപ്പ് രേഖപ്പെടുത്തൽ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ഓഡിയോ പങ്കിടുന്നു"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"പ്രക്ഷേപണം ചെയ്യുന്നു"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബ്രോഡ്‌കാസ്റ്റ് ചെയ്യുന്നത് അവസാനിപ്പിക്കണോ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"നിങ്ങൾ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്‌കാസ്റ്റ് ചെയ്യുകയോ ഔട്ട്പുട്ട് മാറ്റുകയോ ചെയ്താൽ നിങ്ങളുടെ നിലവിലുള്ള ബ്രോഡ്‌കാസ്റ്റ് അവസാനിക്കും"</string>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index 62bac5cd1212..529d0def3e27 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ഓഫാണ്"</item>
<item msgid="578444932039713369">"ഓണാണ്"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"ലഭ്യമല്ല"</item>
<item msgid="8707481475312432575">"ഓഫാണ്"</item>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index f5bf05a4d671..aad6a13c43d7 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Илүү олон виджет нэмэх"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджетүүдийг өөрчлөхийн тулд удаан дарна уу"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджетүүдийг өөрчлөх"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Виджетийг засах"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Хасах"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет нэмэх"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Хонх дуугаргах"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Чичиргэх"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Хаах"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Дамжуулах"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Хонхны дууг хаасан тул боломжгүй"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дууг нь нээхийн тулд товшино уу."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Чичиргээнд тохируулахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Чичиргээнд тохируулахын тулд товшино уу."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дууг хаахын тулд товшино уу."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Шуугианы хяналт"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Хонхны горимыг өөрчлөхийн тулд товшино уу"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дууг хаах"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дууг нээх"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Асаах/унтраах цэс"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>-н <xliff:g id="ID_1">%1$d</xliff:g>-р хуудас"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Түгжээтэй дэлгэц"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Хянамж болгоомжийн алхмыг харах"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Хянамж болгоомжийн алхмыг харах"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Төхөөрөмжөө салгана уу"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Тэмдэглэл хөтлөх"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Тэмдэглэл хөтлөх, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Аудио хуваалцаж байна"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Нэвтрүүлэлт"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г нэвтрүүлэхээ зогсоох уу?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Хэрэв та <xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлсэн эсвэл гаралтыг өөрчилсөн бол таны одоогийн нэвтрүүлэлтийг зогсооно"</string>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 33f359672477..0db122904755 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Унтраалттай"</item>
<item msgid="578444932039713369">"Асаалттай"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Боломжгүй"</item>
<item msgid="8707481475312432575">"Унтраалттай"</item>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index f9d4aca9efcd..d7acf5f5fea2 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"आणखी विजेट जोडा"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट कस्टमाइझ करण्यासाठी प्रेस करून ठेवा"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेट कस्टमाइझ करा"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"विजेट संपादित करा"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"काढून टाका"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट जोडा"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"रिंग करा"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"व्हायब्रेट"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"म्यूट करा"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"कास्ट करा"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"रिंग म्यूट केल्यामुळे उपलब्ध नाही"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करण्यासाठी टॅप करा."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा म्यूट केल्या जाऊ शकतात."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. म्यूट करण्यासाठी टॅप करा."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"नॉइझ कंट्रोल"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलण्यासाठी टॅप करा"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"पॉवर मेनू"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> पैकी <xliff:g id="ID_1">%1$d</xliff:g> पेज"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"लॉक स्‍क्रीन"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"काय काळजी घ्यावी ते पहा"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"काय काळजी घ्यावी ते पहा"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"तुमचे डिव्हाइस अनप्लग करा"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"नोंद घेणे"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"नोंद घेणे, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ऑडिओ शेअर करत आहे"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट करत आहे"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> चे प्रसारण थांबवायचे आहे का?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तुम्ही <xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण केल्यास किंवा आउटपुट बदलल्यास, तुमचे सध्याचे प्रसारण बंद होईल"</string>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index 24d3b47af961..b70a5cc671a7 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"बंद आहे"</item>
<item msgid="578444932039713369">"सुरू आहे"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"उपलब्ध नाही"</item>
<item msgid="8707481475312432575">"बंद आहे"</item>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index dafcfe009769..03cd0a0fab13 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Tambahkan lagi widget"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tekan lama untuk menyesuaikan widget"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Sesuaikan widget"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Alih keluar"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Dering"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Getar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Redam"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Hantar"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia kerana deringan diredam"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketik untuk menyahredam."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketik untuk menetapkan pada getar. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketik untuk meredam. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Ketik untuk menetapkan pada getar."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ketik untuk meredam."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kawalan Hingar"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketik untuk menukar mod pendering"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"redam"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"nyahredam"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu kuasa"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> daripada <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Kunci skrin"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Lihat langkah penjagaan"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Lihat langkah penjagaan"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Cabut palam peranti anda"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Pengambilan nota"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pengambilan nota, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Perkongsian audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika anda siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau tukarkan output, siaran semasa anda akan berhenti"</string>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index 07a8426feeb2..b72a3756ccaa 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Mati"</item>
<item msgid="578444932039713369">"Hidup"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Tidak tersedia"</item>
<item msgid="8707481475312432575">"Mati"</item>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index e52b899870d2..c6d46bccf254 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"နောက်ထပ်ဝိဂျက်များ ထည့်ရန်"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ဝိဂျက်များ စိတ်ကြိုက်လုပ်ရန် ကြာကြာနှိပ်ထားပါ"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ဝိဂျက်များကို စိတ်ကြိုက်လုပ်ရန်"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ဝိဂျက်ပြင်ရန်"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ဖယ်ရှားရန်"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ဝိဂျက်ထည့်ရန်"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"အသံမြည်သည်"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"တုန်ခါသည်"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"အသံတိတ်သည်"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"ကာစ်လုပ်ရန်"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ဖုန်းမြည်သံပိတ်ထားသဖြင့် မရနိုင်ပါ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s။ အသံပြန်ဖွင့်ရန် တို့ပါ။"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s။ တုန်ခါမှုကို သတ်မှတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s။ တုန်ခါခြင်းသို့ သတ်မှတ်ရန်တို့ပါ။"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s။ အသံပိတ်ရန် တို့ပါ။"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ဆူညံသံ ထိန်းချုပ်ရန်"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံပိတ်ရန်"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ပါဝါမီနူး"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"စာမျက်နှာ <xliff:g id="ID_2">%2$d</xliff:g> အနက်မှ စာမျက်နှာ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"လော့ခ်ချထားချိန် မျက်နှာပြင်"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"သင့်စက်ကို ပလတ်ဖြုတ်ပါ"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>၊ <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"မှတ်စုလိုက်ခြင်း"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"မှတ်စုလိုက်ခြင်း၊ <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"အသံမျှဝေခြင်း"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ထုတ်လွှင့်ခြင်း"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ထုတ်လွှင့်ခြင်းကို ရပ်မလား။"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ကို ထုတ်လွှင့်သောအခါ (သို့) အထွက်ကို ပြောင်းသောအခါ သင့်လက်ရှိထုတ်လွှင့်ခြင်း ရပ်သွားမည်"</string>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index fd375d4f3eb1..d223dc9d0bed 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ပိတ်"</item>
<item msgid="578444932039713369">"ဖွင့်"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"မရနိုင်ပါ"</item>
<item msgid="8707481475312432575">"ပိတ်"</item>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ed6e2931ed5f..a2b97eee2fbe 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Legg til flere moduler"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Trykk lenge for å tilpasse modulene"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tilpass moduler"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Endre modul"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Legg til modul"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrer"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ignorer"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Utilgjengelig fordi ringelyden er kuttet"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trykk for å slå på lyden."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trykk for å angi vibrasjon. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Trykk for å angi vibrasjon."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Trykk for å slå av lyden."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Støykontroll"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trykk for å endre ringemodus"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"kutt lyden"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på lyden"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Av/på-meny"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Side <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Låseskjerm"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Se vedlikeholdstrinnene"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Se vedlikeholdstrinnene"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Koble fra enheten"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Notatskriving"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notatskriving, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Deler lyd"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Kringkaster"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vil du stoppe kringkastingen av <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du kringkaster <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller endrer utgangen, stopper den nåværende kringkastingen din"</string>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index e4a811941b2a..2ed00960fdfd 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Av"</item>
<item msgid="578444932039713369">"På"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Utilgjengelig"</item>
<item msgid="8707481475312432575">"Av"</item>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index fb7680daf152..1388a85a6f15 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"थप विजेटहरू हाल्नुहोस्"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेटहरू कस्टमाइज गर्न केही बेरसम्म थिच्नुहोस्"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेटहरू कस्टमाइज गर्नुहोस्"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"विजेट सम्पादन गर्नुहोस्"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"हटाउनुहोस्"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट हाल्नुहोस्"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"घन्टी"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"कम्पन"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"म्युट गर्नुहोस्"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"कास्ट गर्नुहोस्"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"डिभाइस म्युट गरिएकाले यो सुविधा उपलब्ध छैन"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। अनम्यूट गर्नाका लागि ट्याप गर्नुहोस्।"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। कम्पनमा सेट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। म्यूट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। कम्पन मोडमा सेट गर्न ट्याप गर्नुहोस्।"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। म्यूट गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"नोइज कन्ट्रोल"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिङ्गर मोड बदल्न ट्याप गर्नुहोस्"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्युट गर्नुहोस्"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्युट गर्नुहोस्"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"पावर मेनु"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> मध्ये पृष्ठ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"लक स्क्रिन"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"डिभाइसको हेरचाह गर्ने तरिका हेर्नुहोस्"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"डिभाइसको हेरचाह गर्ने तरिका हेर्नुहोस्"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"डिभाइस बिजुलीको स्रोतबाट निकाल्नुहोस्"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"नोट लेख्ने कार्य"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"नोट लेख्ने कार्य, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"अडियो सेयर गरिँदै छ"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"प्रसारण गरिँदै छ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ब्रोडकास्ट गर्न छाड्ने हो?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तपाईंले <xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुभयो वा आउटपुट परिवर्तन गर्नुभयो भने तपाईंको हालको ब्रोडकास्ट रोकिने छ"</string>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 5cf91e533494..40159fb17279 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"अफ छ"</item>
<item msgid="578444932039713369">"अन छ"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"उपलब्ध छैन"</item>
<item msgid="8707481475312432575">"अफ छ"</item>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index a649617a5559..09156cf074e6 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Meer widgets toevoegen"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Houd lang ingedrukt om widgets aan te passen"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widgets aanpassen"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Widget bewerken"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Verwijderen"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget toevoegen"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Bellen"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Trillen"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Geluid staat uit"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Casten"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niet beschikbaar, belgeluid staat uit"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om dempen op te heffen."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tik om in te stellen op trillen."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tik om geluid uit te zetten."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Ruisonderdrukking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Aan/uit-menu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Vergrendelscherm"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Onderhoudsstappen bekijken"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Onderhoudsstappen bekijken"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Je apparaat loskoppelen"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Aantekeningen maken"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Aantekeningen maken, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audio delen"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitzending"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Uitzending van <xliff:g id="APP_NAME">%1$s</xliff:g> stopzetten?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Als je <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzendt of de uitvoer wijzigt, wordt je huidige uitzending gestopt"</string>
@@ -1236,9 +1246,9 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standaard notitie-app instellen in Instellingen"</string>
<string name="install_app" msgid="5066668100199613936">"App installeren"</string>
<string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Swipe omhoog om door te gaan"</string>
- <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spiegelen naar extern scherm?"</string>
+ <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirroren naar extern scherm?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Het scherm aan de binnenkant wordt gemirrord. Het scherm aan de voorkant wordt uitgezet."</string>
- <string name="mirror_display" msgid="2515262008898122928">"Scherm spiegelen"</string>
+ <string name="mirror_display" msgid="2515262008898122928">"Scherm mirroren"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Sluiten"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Scherm verbonden"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microfoon en camera"</string>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index 592ecf5b69d6..60e35da7611b 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Uit"</item>
<item msgid="578444932039713369">"Aan"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Niet beschikbaar"</item>
<item msgid="8707481475312432575">"Uit"</item>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index b0f5022c9c17..b49c2979ac43 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -287,7 +287,7 @@
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"ମିଡିଆ ଡିଭାଇସ୍‌"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"ୟୁଜର"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string>
- <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟରନେଟ"</string>
+ <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟର୍ନେଟ"</string>
<string name="quick_settings_networks_available" msgid="1875138606855420438">"ନେଟୱାର୍କଗୁଡ଼ିକ ଉପଲବ୍ଧ"</string>
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"କୌଣସି ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ଅଧିକ ୱିଜେଟ ଯୋଗ କରନ୍ତୁ"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ୱିଜେଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରିବା ପାଇଁ ଅଧିକ ସମୟ ଦବାନ୍ତୁ"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ୱିଜେଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ୱିଜେଟକୁ ଏଡିଟ କରନ୍ତୁ"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ୱିଜେଟ ଯୋଗ କରନ୍ତୁ"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"ରିଙ୍ଗ"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ଭାଇବ୍ରେଟ୍‌"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"ମ୍ୟୁଟ"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"କାଷ୍ଟ କରନ୍ତୁ"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ରିଂକୁ ମ୍ୟୁଟ କରାଯାଇଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ଅନମ୍ୟୁଟ୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ଭାଇବ୍ରେଟ୍‍ ସେଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍‌ ମ୍ୟୁଟ୍‍ କରାଯାଇପାରେ।"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ମ୍ୟୁଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍‌ ମ୍ୟୁଟ୍‍ କରାଯାଇପାରେ।"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। ଭାଇବ୍ରେଟରେ ସେଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। ମ୍ୟୁଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ନଏଜ କଣ୍ଟ୍ରୋଲ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ରିଙ୍ଗର୍ ମୋଡ୍ ବଦଳାଇବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ମ୍ୟୁଟ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ଅନ୍‍-ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ପାୱାର ମେନୁ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ପୃଷ୍ଠା <xliff:g id="ID_1">%1$d</xliff:g> ମୋଟ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"ଲକ ସ୍କ୍ରିନ"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ଯତ୍ନ ନେବା ପାଇଁ ଷ୍ଟେପଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ଯତ୍ନ ନେବା ପାଇଁ ଷ୍ଟେପଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ଆପଣଙ୍କ ଡିଭାଇସକୁ ଅନପ୍ଲଗ କରନ୍ତୁ"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"ନୋଟ-ଟେକିଂ"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ନୋଟ-ଟେକିଂ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ବ୍ରଡକାଷ୍ଟ କରୁଛି"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ଯଦି ଆପଣ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତି କିମ୍ବା ଆଉଟପୁଟ ବଦଳାନ୍ତି, ତେବେ ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ବ୍ରଡକାଷ୍ଟ ବନ୍ଦ ହୋଇଯିବ"</string>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index d362c65fa971..43bddbf9f49b 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ବନ୍ଦ ଅଛି"</item>
<item msgid="578444932039713369">"ଚାଲୁ ଅଛି"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"ଉପଲବ୍ଧ ନାହିଁ"</item>
<item msgid="8707481475312432575">"ବନ୍ଦ ଅଛି"</item>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 6916fd2b3be9..cd55198317e8 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ਹੋਰ ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ਵਿਜੇਟਾਂ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ਵਿਜੇਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"ਵਿਜੇਟ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ਹਟਾਓ"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"ਘੰਟੀ"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"ਥਰਥਰਾਹਟ"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"ਮਿਊਟ"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"ਕਾਸਟ ਕਰੋ"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਘੰਟੀ ਮਿਊਟ ਕੀਤੀ ਹੋਈ ਹੈ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ਅਣਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ਥਰਥਰਾਹਟ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। ਥਰਥਰਾਹਟ \'ਤੇ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ਸ਼ੋਰ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ਰਿੰਗਰ ਮੋਡ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ਮਿਊਟ ਕਰੋ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ਅਣਮਿਊਟ ਕਰੋ"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ਪਾਵਰ ਮੀਨੂ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ਦਾ <xliff:g id="ID_1">%1$d</xliff:g> ਪੰਨਾ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">" ਲਾਕ ਸਕ੍ਰੀਨ"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ਦੇਖਭਾਲ ਦੇ ਪੜਾਅ ਦੇਖੋ"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ਦੇਖਭਾਲ ਦੇ ਪੜਾਅ ਦੇਖੋ"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ਆਪਣਾ ਡੀਵਾਈਸ ਅਣਪਲੱਗ ਕਰੋ"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"ਨੋਟ ਬਣਾਉਣਾ"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ਨੋਟ ਬਣਾਉਣਾ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ਪ੍ਰਸਾਰਨ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੇ ਪ੍ਰਸਾਰਨ ਨੂੰ ਰੋਕਣਾ ਹੈ?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ਜੇ ਤੁਸੀਂ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਦੇ ਹੋ ਜਾਂ ਆਊਟਪੁੱਟ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਪ੍ਰਸਾਰਨ ਰੁਕ ਜਾਵੇਗਾ"</string>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index f249afb72b6d..5f0ca172dca9 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ਬੰਦ ਹੈ"</item>
<item msgid="578444932039713369">"ਚਾਲੂ ਹੈ"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"ਅਣਉਪਲਬਧ ਹੈ"</item>
<item msgid="8707481475312432575">"ਬੰਦ ਹੈ"</item>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index c04d6dae6e0d..76547ed5266c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodaj więcej widżetów"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Przytrzymaj, aby dostosować widżety"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Dostosuj widżety"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Edytuj widżet"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Usuń"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widżet"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Dzwonek"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Wibracje"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Wyciszenie"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Przesyłanie"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niedostępne, bo dzwonek jest wyciszony"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Kliknij, by wyłączyć wyciszenie."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Kliknij, by włączyć wibracje. Ułatwienia dostępu mogą być wyciszone."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Kliknij, by włączyć wibracje."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Kliknij, by wyciszyć."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Tłumienie szumów"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Kliknij, aby zmienić tryb dzwonka"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"wycisz"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"wyłącz wyciszenie"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu zasilania"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Strona <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran blokady"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Zobacz instrukcję postępowania"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobacz instrukcję postępowania"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Odłącz urządzenie"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Notatki"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notatki, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Udostępnia dźwięk"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmisja"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zatrzymaj transmisję aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jeśli transmitujesz aplikację <xliff:g id="SWITCHAPP">%1$s</xliff:g> lub zmieniasz dane wyjściowe, Twoja obecna transmisja zostanie zakończona"</string>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index ea6ad58c825a..5e3a1189c940 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Wyłączony"</item>
<item msgid="578444932039713369">"Włączony"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Niedostępne"</item>
<item msgid="8707481475312432575">"Wyłączone"</item>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e82a1032dbc6..598ef78ae63b 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicione mais widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha pressionado para personalizar widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Tocar"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Desativar som"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Transmitir"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque foi silenciado"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toque para configurar para vibrar."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toque para silenciar."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Controle de ruído"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu liga/desliga"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Tela de bloqueio"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver etapas de cuidado"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver etapas de cuidado"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconecte seu dispositivo"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Anotações"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Anotações, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Compartilhando áudio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index 28c07f40b6b9..d4fd83803ba7 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Desativada"</item>
<item msgid="578444932039713369">"Ativada"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Indisponível"</item>
<item msgid="8707481475312432575">"Desativado"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8ba907b4ee41..7e5a7368faaf 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicionar mais widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha premido para personalizar os widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícone da app do widget desativado"</string>
<string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Toque"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Desativar som"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Transmitir"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível porque o toque está desat."</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para reativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para ativar a vibração. Os serviços de acessibilidade podem ser silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toque para ativar a vibração."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toque para desativar o som."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Controlo de ruído"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para alterar o modo de campainha"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"reativar som"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu ligar/desligar"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ecrã de bloqueio"</string>
+ <string name="finder_active" msgid="7907846989716941952">"Pode localizar este telemóvel com o serviço Localizar o meu dispositivo mesmo quando está desligado"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"A encerrar…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Veja os passos de manutenção"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Veja os passos de manutenção"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Desligue o dispositivo"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Tomar notas"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Tomar notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"A partilhar áudio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"A transmitir"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão da app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se transmitir a app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou alterar a saída, a sua transmissão atual é interrompida"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index b58b8488a3e4..e94b1ec6b0fd 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Desligado"</item>
<item msgid="578444932039713369">"Ligado"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Indisponível"</item>
<item msgid="8707481475312432575">"Desligado"</item>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e82a1032dbc6..598ef78ae63b 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicione mais widgets"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha pressionado para personalizar widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Editar widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Tocar"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Desativar som"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Transmitir"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque foi silenciado"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toque para configurar para vibrar."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toque para silenciar."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Controle de ruído"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu liga/desliga"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Tela de bloqueio"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Ver etapas de cuidado"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver etapas de cuidado"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconecte seu dispositivo"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Anotações"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Anotações, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Compartilhando áudio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index 28c07f40b6b9..d4fd83803ba7 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Desativada"</item>
<item msgid="578444932039713369">"Ativada"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Indisponível"</item>
<item msgid="8707481475312432575">"Desativado"</item>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 8cd2d77f9efb..026e22bd6328 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adaugă mai multe widgeturi"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Apasă lung pentru a personaliza widgeturi"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizează widgeturile"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Editează widgetul"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Elimină"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adaugă un widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonerie"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrații"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Blochează"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Proiectează"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponibil; soneria este dezactivată"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atinge pentru a activa sunetul."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atinge pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atinge pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Atinge pentru a seta pe vibrații."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Atinge pentru a dezactiva sunetul."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Controlul zgomotului"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meniul de pornire"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> din <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ecran de blocare"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Vezi pașii pentru îngrijire"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vezi pașii pentru îngrijire"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Deconectează dispozitivul"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Luare de notițe"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Luare de notițe, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Se permite accesul la conținutul audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Se difuzează"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Oprești transmisia <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Dacă transmiți <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbi ieșirea, transmisia actuală se va opri"</string>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 5a5eb9f6788c..75565f942ac4 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Dezactivată"</item>
<item msgid="578444932039713369">"Activată"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Indisponibilă"</item>
<item msgid="8707481475312432575">"Dezactivată"</item>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 614eb0617fb4..1df112ba8493 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Добавить виджеты"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Нажмите и удерживайте, чтобы настроить виджеты."</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Настроить виджеты"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Изменить виджет"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Удалить"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Добавить виджет"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Со звуком"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрация"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Без звука"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Трансляция"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно, когда отключен звук вызовов"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Нажмите, чтобы включить звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Нажмите, чтобы включить вибрацию. Специальные возможности могут прекратить работу."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Нажмите, чтобы включить вибрацию."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Нажмите, чтобы выключить звук."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контроль уровня шума"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Нажмите, чтобы изменить режим звонка."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"включить звук"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню кнопки питания"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> из <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заблокированный экран"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Подробнее о действиях при перегреве…"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Подробнее о действиях при перегреве…"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Отключите устройство"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Создание заметок"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>: создание заметок"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Передает аудио"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляция"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Остановить трансляцию \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Если вы начнете транслировать \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" или смените целевое устройство, текущая трансляция прервется."</string>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index cd140791bbd5..3099e008c93a 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Откл."</item>
<item msgid="578444932039713369">"Вкл."</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Функция недоступна"</item>
<item msgid="8707481475312432575">"Откл."</item>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index d1bcee422bd4..f4b7d1eb1254 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"තවත් විජට් එක් කරන්න"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"විජට් අභිරුචිකරණය කිරීමට දිගු ඔබන්න"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"විජට්ටු අභිරුචි කරන්න"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"විජට්ටු සංස්කරණ කරන්න"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ඉවත් කරන්න"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"විජට්ටුව එක් කරන්න"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"නාද කරන්න"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"කම්පනය කරන්න"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"නිහඬ කරන්න"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"විකාශය"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"නාදය නිහඬ කර ඇති නිසා නොලැබේ"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. නිහඬ කිරීම ඉවත් කිරීමට තට්ටු කරන්න."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. කම්පනය කිරීමට තට්ටු කරන්න. ප්‍රවේශ්‍යතා සේවා නිහඬ කළ හැකිය."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්‍රවේශ්‍යතා සේවා නිහඬ කළ හැකිය."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. කම්පනය කිරීමට සකස් කිරීමට තට්ටු කරන්න."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ඝෝෂාව පාලනය"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"නාදකය වෙනස් කිරීමට තට්ටු කරන්න"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"නිහඬ කරන්න"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"නිශ්ශබ්දතාවය ඉවත් කරන්න"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"බල මෙනුව"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> න් <xliff:g id="ID_1">%1$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"අගුලු තිරය"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"රැකවරණ පියවර බලන්න"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"රැකවරණ පියවර බලන්න"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ඔබේ උපාංගය ගලවන්න"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"සටහන් කර ගැනීම"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"සටහන් කර ගැනීම, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ශ්‍රව්‍ය බෙදා ගැනීම"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"විකාශනය කරමින්"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> විකාශනය කිරීම නවත්වන්නද?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ඔබ <xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය කළහොත් හෝ ප්‍රතිදානය වෙනස් කළහොත්, ඔබගේ වත්මන් විකාශනය නවතිනු ඇත."</string>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index fcd768bfc4e3..48e8cc4af507 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"අක්‍රියයි"</item>
<item msgid="578444932039713369">"සක්‍රියයි"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"නොමැත"</item>
<item msgid="8707481475312432575">"අක්‍රියයි"</item>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 3e2b554f18b2..0f579da44ed6 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Pridať ďalšie miniaplikácie"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Miniaplikácie prispôsobíte dlhým stlačením"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prispôsobiť miniaplikácie"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Upraviť miniaplikáciu"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Odstrániť"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pridať miniaplikáciu"</string>
@@ -530,7 +532,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Toto zariadenie spravuje tvoj rodič. Vidí a môže spravovať informácie, napríklad aplikácie, ktoré používaš, tvoju polohu a čas používania."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Odomknutie udržiava TrustAgent"</string>
- <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrana pred krádež.\nZar. uzamknuté, priveľa pokusov o odomk."</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrana pred krádežou\nUzamknuté, priveľa pokusov o odomknutie"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Nastavenia zvuku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatické titulkovanie médií"</string>
@@ -573,19 +575,22 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Prezvoniť"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrovať"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Vypnúť zvuk"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Prenos"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, pretože je vypnuté zvonenie"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnite zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujte režim vibrovania. Služby dostupnosti je možné stlmiť."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Klepnutím nastavíte vibrovanie."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Klepnutím vypnete zvuk."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Ovládanie šumu"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Režim zvonenia zmeníte klepnutím"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Ovládacie prvky hlasitosti %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Hovory a upozornenia spustia zvonenie (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
- <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> sa prehráva v zar."</string>
- <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk prehrá zariad.:"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> sa prehráva v:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk sa prehrá v:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tuner používateľského rozhrania systému"</string>
<string name="status_bar" msgid="4357390266055077437">"Stavový riadok"</string>
<string name="demo_mode" msgid="263484519766901593">"Ukážka používateľského rozhrania systému"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Ponuka vypínača"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Strana <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Uzamknutá obrazovka"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Zobraziť opatrenia"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobraziť opatrenia"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Odpojte zariadenie"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Zapisovanie poznámok"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Zapisovanie poznámok, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Zdieľa sa zvuk"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysiela"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Chcete zastaviť vysielanie aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ak vysielate aplikáciu <xliff:g id="SWITCHAPP">%1$s</xliff:g> alebo zmeníte výstup, aktuálne vysielanie bude zastavené"</string>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 660f85d365e2..fdfcd27db263 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Vypnuté"</item>
<item msgid="578444932039713369">"Zapnuté"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nie je k dispozícii"</item>
<item msgid="8707481475312432575">"Vypnuté"</item>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 1ea97a1f4b43..7acaa54cbbad 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte več pripomočkov"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pridržite za prilagajanje pripomočkov"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagajanje pripomočkov"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Urejanje pripomočka"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Odstrani"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajanje pripomočka"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zvonjenje"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibriranje"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Utišano"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Predvajanje"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ni na voljo, ker je zvonjenje izklopljeno"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dotaknite se, če želite vklopiti zvok."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dotaknite se, če želite nastaviti vibriranje. V storitvah za dostopnost bo morda izklopljen zvok."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za dostopnost bo morda izklopljen zvok."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dotaknite se, če želite nastaviti vibriranje."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dotaknite se, če želite izklopiti zvok."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Omejevanje hrupa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meni za vklop/izklop"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. stran od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Zaklenjen zaslon"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Oglejte si navodila za ukrepanje"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Oglejte si navodila za ukrepanje"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Odklopite napravo"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Ustvarjanje zapiskov"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ustvarjanje zapiskov, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Deljenje zvoka"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Oddajanje"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite ustaviti oddajanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Če oddajate aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g> ali spremenite izhod, bo trenutno oddajanje ustavljeno."</string>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index d7e62caac28d..3804d639bb52 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Izklopljeno"</item>
<item msgid="578444932039713369">"Vklopljeno"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Ni na voljo"</item>
<item msgid="8707481475312432575">"Izklopljeno"</item>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index c3ccd81e6f62..21a974faf29e 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Shto miniaplikacione të tjera"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Shtyp gjatë për të personalizuar miniaplikacionet"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizo miniaplikacionet"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Modifiko miniaplikacionin"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Hiq"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Shto miniaplikacionin"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Bjeri ziles"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Dridhje"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Pa zë"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Transmeto"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nuk ofrohet; ziles i është hequr zëri"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trokit për të aktivizuar."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trokit për ta caktuar te dridhja. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Trokit për ta vendosur në dridhje."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Trokit për ta çaktivizuar."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kontrolli i zhurmës"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trokit për të ndryshuar modalitetin e ziles"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"çaktivizo audion"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktivizo audion"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menyja e energjisë"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Faqja <xliff:g id="ID_1">%1$d</xliff:g> nga <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekrani i kyçjes"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Shiko hapat për kujdesin"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Shiko hapat për kujdesin"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Shkëpute pajisjen"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Mbajtja e shënimeve"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Mbajtja e shënimeve, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Po ndahet audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Po transmeton"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Të ndalohet transmetimi i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nëse transmeton <xliff:g id="SWITCHAPP">%1$s</xliff:g> ose ndryshon daljen, transmetimi yt aktual do të ndalojë"</string>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index b8e1355dfb25..6318700bf6f2 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Joaktive"</item>
<item msgid="578444932039713369">"Aktiv"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Nuk ofrohet"</item>
<item msgid="8707481475312432575">"Joaktive"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 2309d7de205c..2f52f90b3622 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додајте још виџета"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Дуги притисак за прилагођавање виџета"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Прилагоди виџете"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Измени виџет"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Уклони"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додај виџет"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Активирај звоно"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вибрирај"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Искључи звук"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Пребацивање"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно јер је звук искључен"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Додирните да бисте укључили звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Додирните да бисте подесили на вибрацију. Звук услуга приступачности ће можда бити искључен."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Додирните да бисте подесили на вибрацију."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Додирните да бисте искључили звук."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контрола шума"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Додирните да бисте променили режим звона"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"искључите звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"укључите звук"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Мени дугмета за укључивање"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. страна од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Закључан екран"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Погледајте упозорења"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Погледајте упозорења"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Искључите уређај"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Прављење бележака"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Прављење бележака, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Дели се звук"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитовање"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Желите да зауставите емитовање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитујете апликацију <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените излаз, актуелно емитовање ће се зауставити"</string>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index c959bfb0eb73..e4cf0b6cd2c0 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Искључено"</item>
<item msgid="578444932039713369">"Укључено"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Недоступно"</item>
<item msgid="8707481475312432575">"Искључено"</item>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index be80f7aefd2d..20bc7e70a0da 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lägg till fler widgetar"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tryck länge för att anpassa widgetar"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Anpassa widgetar"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Redigera widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Ta bort"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lägg till widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ringsignal"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibration"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Dölj"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Casta"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Otillgängligt eftersom ringljudet är av"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryck här om du vill slå på ljudet."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryck här om du vill sätta på vibrationen. Tillgänglighetstjänster kanske inaktiveras."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tryck här om du vill aktivera vibrationsläget."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tryck här om du vill stänga av ljudet."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Bruskontroll"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryck för att ändra ringsignalens läge"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stänga av ljudet"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på ljudet"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Startmeny"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sida <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Låsskärm"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Visa alla skötselråd"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Visa alla skötselråd"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Koppla ur enheten"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Anteckna"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Anteckna med <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Delar ljud"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sänder"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vill du sluta sända från <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Om en utsändning från <xliff:g id="SWITCHAPP">%1$s</xliff:g> pågår eller om du byter ljudutgång avbryts den nuvarande utsändningen"</string>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index 28717dfa5229..8981ac775e50 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Av"</item>
<item msgid="578444932039713369">"På"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Inte tillgängligt"</item>
<item msgid="8707481475312432575">"Av"</item>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 09998200c0f4..fa0e7b5eba05 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Weka wijeti zingine"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Bonyeza kwa muda mrefu uweke mapendeleo ya wijeti"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Badilisha wijeti upendavyo"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Badilisha wijeti"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Ondoa"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ongeza wijeti"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Piga"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Kutetema"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Zima sauti"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Tuma"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Halipatikani kwa sababu sauti imezimwa"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Gusa ili urejeshe."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Gusa ili uweke mtetemo."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Gusa ili usitishe."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kidhibiti cha Kelele"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"zima sauti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"washa sauti"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menyu ya kuzima/kuwasha"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Ukurasa wa <xliff:g id="ID_1">%1$d</xliff:g> kati ya <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Skrini iliyofungwa"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Angalia hatua za utunzaji"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Angalia hatua za utunzaji"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Chomoa kifaa chako"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Kuandika madokezo"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Kuandika madokezo, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Watu wengine wanasikia sauti"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Inaarifu"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ungependa kusimamisha utangazaji kwenye <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ikiwa unatangaza kwenye <xliff:g id="SWITCHAPP">%1$s</xliff:g> au unabadilisha maudhui, tangazo lako la sasa litasimamishwa"</string>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index 2fe40603bc7d..08a1f149264c 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Kimezimwa"</item>
<item msgid="578444932039713369">"Kimewashwa"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Hakipatikani"</item>
<item msgid="8707481475312432575">"Kimezimwa"</item>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 486d7f97f16e..0f360e6bc17b 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"கூடுதல் விட்ஜெட்களைச் சேருங்கள்"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"விட்ஜெட்களைப் பிரத்தியேகமாக்க நீண்ட நேரம் அழுத்துக"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"விட்ஜெட்களைப் பிரத்தியேகமாக்குங்கள்"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"விட்ஜெட்டைத் திருத்து"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"அகற்றும்"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"விட்ஜெட்டைச் சேர்"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"ஒலி"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"அதிர்வு"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"அமைதி"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"அலைபரப்பு"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"\'ரிங்\' மியூட்டில் உள்ளதால் கிடைக்கவில்லை"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ஒலி இயக்க, தட்டவும்."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும்."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. ஒலியடக்க, தட்டவும்."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"இரைச்சல் கட்டுப்பாடு"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ரிங்கர் பயன்முறையை மாற்ற தட்டவும்"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ஒலியடக்கும்"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ஒலி இயக்கும்"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"பவர் மெனு"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"பக்கம் <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"லாக் ஸ்கிரீன்"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"மேலும் விவரங்களுக்கு இதைப் பார்க்கவும்"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"மேலும் விவரங்களுக்கு இதைப் பார்க்கவும்"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"சாதன இணைப்பைத் துண்டித்தல்"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"குறிப்பெடுத்தல்"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"குறிப்பெடுத்தல், <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ஆடியோ பகிரப்படுகிறது"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ஒலிபரப்புதல்"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒலிபரப்பப்படுவதை நிறுத்தவா?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"நீங்கள் <xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பினாலோ அவுட்புட்டை மாற்றினாலோ உங்களின் தற்போதைய ஒலிபரப்பு நிறுத்தப்படும்"</string>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index 5bcc6c761e1c..741d6dea191f 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="578444932039713369">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"கிடைக்கவில்லை"</item>
<item msgid="8707481475312432575">"முடக்கப்பட்டுள்ளது"</item>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 227ba5a2ea4a..4e8e2d0e4ca8 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"మరిన్ని విడ్జెట్‌లను జోడించండి"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"విడ్జెట్‌లను అనుకూలీకరించడానికి, నొక్కి, ఉంచండి"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"విడ్జెట్‌లను అనుకూలంగా మార్చండి"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"విడ్జెట్‌ను ఎడిట్ చేయండి"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"తీసివేయండి"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"విడ్జెట్‌ను జోడించండి"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"రింగ్"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"వైబ్రేట్"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"మ్యూట్"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"ప్రసారం చేయండి"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"వాల్యూమ్ మ్యూట్ అయినందున అందుబాటులో లేదు"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. అన్‌మ్యూట్ చేయడానికి నొక్కండి."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. వైబ్రేషన్‌కు సెట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. వైబ్రేట్ అయ్యేలా సెట్ చేయడం కోసం నొక్కండి."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. మ్యూట్ చేయడానికి నొక్కండి."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"నాయిస్ కంట్రోల్"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"రింగర్ మోడ్‌ను మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"మ్యూట్ చేయి"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్‌మ్యూట్ చేయి"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"పవర్ మెనూ"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>లో <xliff:g id="ID_1">%1$d</xliff:g>వ పేజీ"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"లాక్ స్క్రీన్"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"మీ పరికరాన్ని అన్‌ప్లగ్ చేయండి"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"నోట్-టేకింగ్"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"నోట్-టేకింగ్, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"ఆడియోను షేర్ చేస్తున్నారు"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ప్రసారం చేస్తోంది"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రసారం చేయడాన్ని ఆపివేయాలా?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"మీరు <xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేస్తే లేదా అవుట్‌పుట్‌ను మార్చినట్లయితే, మీ ప్రస్తుత ప్రసారం ఆగిపోతుంది"</string>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 9d2b407ccd10..6ff293479860 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ఆఫ్‌లో ఉంది"</item>
<item msgid="578444932039713369">"ఆన్‌లో ఉంది"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"అందుబాటులో లేదు"</item>
<item msgid="8707481475312432575">"ఆఫ్‌లో ఉంది"</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d1e186fa73b4..98075c557bca 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"เพิ่มวิดเจ็ตอีก"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"กดค้างเพื่อปรับแต่งวิดเจ็ต"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ปรับแต่งวิดเจ็ต"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"แก้ไขวิดเจ็ต"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"นำออก"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"เพิ่มวิดเจ็ต"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"ทำให้ส่งเสียง"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"สั่น"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"ปิดเสียง"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"แคสต์"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"เปลี่ยนไม่ได้เนื่องจากปิดเสียงเรียกเข้า"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s แตะเพื่อเปิดเสียง"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s แตะเพื่อตั้งค่าให้สั่น อาจมีการปิดเสียงบริการการเข้าถึง"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s แตะเพื่อปิดเสียง อาจมีการปิดเสียงบริการการเข้าถึง"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s แตะเพื่อตั้งค่าให้สั่น"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s แตะเพื่อปิดเสียง"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"การควบคุมเสียง"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"แตะเพื่อเปลี่ยนโหมดเสียงเรียกเข้า"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ปิดเสียง"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"เปิดเสียง"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"เมนูเปิด/ปิด"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"หน้า <xliff:g id="ID_1">%1$d</xliff:g> จาก <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"หน้าจอล็อก"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"ดูขั้นตอนในการดูแลรักษา"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ดูขั้นตอนในการดูแลรักษา"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"ถอดปลั๊กอุปกรณ์"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"การจดบันทึก"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"การจดบันทึก <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"กำลังแชร์เสียง"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"กำลังออกอากาศ"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"หยุดการออกอากาศ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"หากคุณออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g> หรือเปลี่ยนแปลงเอาต์พุต การออกอากาศในปัจจุบันจะหยุดลง"</string>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 69449a72196e..d961385ba72e 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"ปิด"</item>
<item msgid="578444932039713369">"เปิด"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"ไม่พร้อมใช้งาน"</item>
<item msgid="8707481475312432575">"ปิด"</item>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index c929e4579df9..ff8d84f589b7 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Magdagdag ng higit pang widget"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pindutin nang matagal para i-customize ang mga widget"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"I-customize ang mga widget"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"I-edit ang widget"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Alisin"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Magdagdag ng widget"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ipa-ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"I-vibrate"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"I-mute"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Mag-cast"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Hindi available dahil naka-mute ang ring"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. I-tap upang i-unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. I-tap upang itakda na mag-vibrate. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. I-tap upang i-mute. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. I-tap upang itakda na mag-vibrate."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. I-tap upang i-mute."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Pagkontrol sa Ingay"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"I-tap para baguhin ang ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"i-mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"i-unmute"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Power menu"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> ng <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Lock screen"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Tingnan ang mga hakbang sa pangangalaga"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Tingnan ang mga hakbang sa pangangalaga"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Bunutin sa saksakan ang device"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Pagtatala"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pagtatala, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Nagse-share ng audio"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Nagbo-broadcast"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ihinto ang pag-broadcast ng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kung magbo-broadcast ka ng <xliff:g id="SWITCHAPP">%1$s</xliff:g> o babaguhin mo ang output, hihinto ang iyong kasalukuyang broadcast"</string>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index 689c2a26ca61..a12c010f766a 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Naka-off"</item>
<item msgid="578444932039713369">"Naka-on"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Hindi available"</item>
<item msgid="8707481475312432575">"Naka-off"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 9407fbbbf3d9..2cdc6e7e1296 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Daha fazla widget ekle"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widget\'ları özelleştirmek için uzun basın"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widget\'ları özelleştir"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Widget\'ı düzenle"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Kaldır"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget ekle"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Zili çaldır"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Titreşim"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Sesi kapat"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Yayınla"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zil sesi kapatıldığı için kullanılamıyor"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sesi açmak için dokunun."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Titreşime ayarlamak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sesi kapatmak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Titreşime ayarlamak için dokunun."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Sesi kapatmak için dokunun."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Gürültü Kontrolü"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Telefon zili modunu değiştirmek için dokunun"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"sesi kapat"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"sesi aç"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Güç menüsü"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sayfa <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Kilit ekranı"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Bakımla ilgili adımlara bakın"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Bakımla ilgili adımlara bakın"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Cihazınızın fişini çekin"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Not alma"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Not alma, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Ses paylaşılıyor"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayınlama"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında anons durdurulsun mu?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapar veya çıkışı değiştirirseniz mevcut anonsunuz duraklatılır"</string>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index a8c7f784920d..c6a8aecf0da1 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Kapalı"</item>
<item msgid="578444932039713369">"Açık"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Kullanılamıyor"</item>
<item msgid="8707481475312432575">"Kapalı"</item>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index b7a4999865de..c89ae75a5ae2 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додати більше віджетів"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Утримуйте, щоб налаштувати віджети"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Налаштувати віджети"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Редагувати віджет"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Видалити"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додати віджет"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Дзвінок"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Вібросигнал"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Без звуку"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Трансляція"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно: звук дзвінків вимкнено"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Торкніться, щоб увімкнути звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Торкніться, щоб налаштувати вібросигнал."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Торкніться, щоб вимкнути звук."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контроль шуму"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Торкніться, щоб змінити режим дзвінка"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"вимкнути звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"увімкнути звук"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню кнопки живлення"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Сторінка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Заблокований екран"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Переглянути запобіжні заходи"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Переглянути запобіжні заходи"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Від’єднайте пристрій"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Створення нотаток"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Створення нотаток, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Відтворюється аудіо"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляція"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Зупинити трансляцію з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Якщо ви зміните додаток (<xliff:g id="SWITCHAPP">%1$s</xliff:g>) або аудіовихід, поточну трансляцію буде припинено"</string>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 4062f1be4c08..a8e1ab8f3f99 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Вимкнено"</item>
<item msgid="578444932039713369">"Увімкнено"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Недоступно"</item>
<item msgid="8707481475312432575">"Вимкнено"</item>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index f9155a0e06e6..3237f32f5347 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -429,6 +429,7 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"مزید ویجٹس شامل کریں"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ویجٹس کو حسب ضرورت بنانے کے لیے لانگ پریس کریں"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ویجیٹس کو حسب ضرورت بنائیں"</string>
+ <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"غیر فعال ویجیٹ کے لئے ایپ آئیکن"</string>
<string name="edit_widget" msgid="9030848101135393954">"ویجیٹ میں ترمیم کریں"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"ہٹائیں"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ویجیٹ شامل کریں"</string>
@@ -573,11 +574,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"رِنگ کریں"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"وائبریٹ"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"خاموش کریں"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"کاسٹ"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"دستیاب نہیں ہے کیونکہ رنگ خاموش ہے"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏‎%1$s۔ آواز چالو کرنے کیلئے تھپتھپائیں۔"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏‎%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏‎%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"‏‎%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"‏‎%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"شور کنٹرول"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"رنگر وضع تبدیل کرنے کیلئے تھپتھپائیں"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"خاموش کریں"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"غیر خاموش کریں"</string>
@@ -832,6 +836,8 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"پاور مینیو"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"صفحہ <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"مقفل اسکرین"</string>
+ <string name="finder_active" msgid="7907846989716941952">"پاور آف ہونے پر بھی آپ میرا آلہ ڈھونڈیں کے ساتھ اس فون کو تلاش کر سکتے ہیں"</string>
+ <string name="shutdown_progress" msgid="5464239146561542178">"بند ہو رہا ہے…"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"نگہداشت کے اقدامات ملاحظہ کریں"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"نگہداشت کے اقدامات ملاحظہ کریں"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"اپنے آلہ کو ان پلگ کریں"</string>
@@ -1186,6 +1192,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"نوٹ لکھنا"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"نوٹ لکھنا، <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"آڈیو کا اشتراک کرنا"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"نشریات"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> براڈکاسٹنگ روکیں؟"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر آپ <xliff:g id="SWITCHAPP">%1$s</xliff:g> براڈکاسٹ کرتے ہیں یا آؤٹ پٹ کو تبدیل کرتے ہیں تو آپ کا موجودہ براڈکاسٹ رک جائے گا"</string>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index bb27b9fbe9f2..6d1e7076da40 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"آف ہے"</item>
<item msgid="578444932039713369">"آن ہے"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"دستیاب نہیں ہے"</item>
<item msgid="8707481475312432575">"آف ہے"</item>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 82b6b460ef67..478fcdb95527 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Koʻproq vidjetlar qoʻshish"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Vidjetlarni sozlash uchun bosib turing"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Vidjetlarni moslashtirish"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Vidjetni tahrirlash"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Olib tashlash"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidjet kiritish"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Jiringlatish"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Tebranish"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ovozsiz"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Translatsiya"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Jiringlash ovozsizligi uchun ishlamaydi"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ovozini yoqish uchun ustiga bosing."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tebranishni yoqish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tebranishni yoqish uchun ustiga bosing."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ovozsiz qilish uchun ustiga bosing."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Shovqin boshqaruvi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Jiringlagich rejimini oʻzgartirish uchun bosing"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ovozsiz qilish"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ovozni yoqish"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Quvvat menyusi"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>-sahifa, jami: <xliff:g id="ID_2">%2$d</xliff:g> ta sahifa"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran qulfi"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Batafsil axborot"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Batafsil axborot"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Qurilmani uzing"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Qayd olish"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>: qayd olish"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Audio ulashuvi"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Signal uzatish"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga translatsiya toʻxtatilsinmi?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Agar <xliff:g id="SWITCHAPP">%1$s</xliff:g> ilovasiga translatsiya qilsangiz yoki ovoz chiqishini oʻzgartirsangiz, joriy translatsiya toʻxtab qoladi"</string>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index bd5ee8951c5b..0558c4a91794 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Oʻchiq"</item>
<item msgid="578444932039713369">"Yoniq"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Ishlamaydi"</item>
<item msgid="8707481475312432575">"Oʻchiq"</item>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 2a8d4cdce031..7e8638b5bdeb 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Thêm tiện ích khác"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nhấn và giữ để tuỳ chỉnh tiện ích"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tuỳ chỉnh tiện ích"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Chỉnh sửa tiện ích"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Xoá"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Thêm tiện ích"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Đổ chuông"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Rung"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Tắt tiếng"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Truyền"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Không hoạt động vì chuông bị tắt"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Nhấn để bật tiếng."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Nhấn để đặt chế độ rung. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Nhấn để đặt chế độ rung."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Nhấn để tắt tiếng."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Kiểm soát tiếng ồn"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Nhấn để thay đổi chế độ chuông"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"tắt tiếng"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"bật tiếng"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Trình đơn nguồn"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Trang <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Màn hình khóa"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Xem các bước chăm sóc"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Xem các bước chăm sóc"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Rút thiết bị ra"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Ghi chú"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ghi chú, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Đang chia sẻ âm thanh"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Phát sóng"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Dừng phát <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nếu bạn phát <xliff:g id="SWITCHAPP">%1$s</xliff:g> hoặc thay đổi đầu ra, phiên truyền phát hiện tại sẽ dừng"</string>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index 201a45b3d445..eee10d330258 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Đang tắt"</item>
<item msgid="578444932039713369">"Đang bật"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Không hoạt động"</item>
<item msgid="8707481475312432575">"Đang tắt"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ddef35f6d1b8..255213858f91 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"添加更多微件"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"长按即可自定义微件"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自定义微件"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"修改微件"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"添加微件"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"响铃"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"振动"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"静音"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"投屏"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"该功能无法使用,因为铃声被静音"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。点按即可取消静音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。点按即可设为振动,但可能会同时将无障碍服务设为静音。"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。点按即可设为振动。"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。点按即可设为静音。"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"噪声控制"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"点按即可更改振铃器模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"静音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消静音"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"电源菜单"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 页,共 <xliff:g id="ID_2">%2$d</xliff:g> 页"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"锁定屏幕"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"查看处理步骤"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看处理步骤"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"拔出设备"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"记事"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"记事,<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"正在分享音频"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"正在广播"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止广播“<xliff:g id="APP_NAME">%1$s</xliff:g>”的内容吗?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容或更改输出来源,当前的广播就会停止"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 3ab2d7a3d1c7..82ab6710729d 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"已关闭"</item>
<item msgid="578444932039713369">"已开启"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"不可用"</item>
<item msgid="8707481475312432575">"已关闭"</item>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index dbf2cc0a25c8..5941dad6903b 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"新增更多小工具"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自訂小工具"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"編輯小工具"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"新增小工具"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"鈴聲"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"震動"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"靜音"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"投放"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設定為靜音,因此無法使用"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕按即可取消靜音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕按即可設為震動。無障礙功能服務可能已經設為靜音。"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。輕按即可設為震動。"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。輕按即可設為靜音。"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"噪音控制"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕按即可變更響鈴模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"電源選單"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁 (共 <xliff:g id="ID_2">%2$d</xliff:g> 頁)"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"螢幕鎖定"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"查看保養步驟"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看保養步驟"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"拔除裝置"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"做筆記"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"做筆記,<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"正在分享音訊"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止廣播「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如要廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止廣播目前的內容"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index 89d66284ede6..6bac27502583 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"已關閉"</item>
<item msgid="578444932039713369">"已開啟"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"無法使用"</item>
<item msgid="8707481475312432575">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d92de1cf6af9..c46d831c741a 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"新增更多小工具"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自訂小工具"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"編輯小工具"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"新增小工具"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"鈴聲"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"震動"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"靜音"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"投放"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設為靜音,因此無法使用"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕觸即可取消靜音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕觸即可設為震動,但系統可能會將無障礙服務一併設為靜音。"</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。輕觸即可設為震動。"</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。輕觸即可設為靜音。"</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"噪音控制"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕觸即可變更鈴聲模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"電源鍵選單"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁,共 <xliff:g id="ID_2">%2$d</xliff:g> 頁"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"鎖定畫面"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"查看處理步驟"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看處理步驟"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"拔除裝置"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"做筆記"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"做筆記,<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"分享音訊"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止播送「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止播送目前的內容"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index a046e3354729..5794bf1703f9 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"已關閉"</item>
<item msgid="578444932039713369">"已開啟"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"無法使用"</item>
<item msgid="8707481475312432575">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9cd70829c30c..0bbac05638cc 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -429,6 +429,8 @@
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Engeza amawijethi engeziwe"</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Cindezela isikhathi eside ukuze wenze ngokwezifiso amawijethi"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Yenza ngokwezifiso amawijethi"</string>
+ <!-- no translation found for icon_description_for_disabled_widget (4693151565003206943) -->
+ <skip />
<string name="edit_widget" msgid="9030848101135393954">"Hlela amawijethi"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Susa"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engeza iwijethi"</string>
@@ -573,11 +575,14 @@
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Khalisa"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Dlidlizela"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Thulisa"</string>
+ <string name="media_device_cast" msgid="4786241789687569892">"Sakaza"</string>
+ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ayitholakali ngoba ukukhala kuthulisiwe"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Thepha ukuze ususe ukuthula."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Thepha ukuze usethe ukudlidliza. Amasevisi okufinyelela angathuliswa."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Thepha ukuze usethele ekudlidlizeni."</string>
<string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Thepha ukuze uthulise."</string>
+ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Ulawulo Lomsindo"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Thepha ukuze ushintshe imodi yokukhala"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"thulisa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"susa ukuthula"</string>
@@ -832,6 +837,10 @@
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Imenyu yamandla"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Ikhasi <xliff:g id="ID_1">%1$d</xliff:g> kwangu-<xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Khiya isikrini"</string>
+ <!-- no translation found for finder_active (7907846989716941952) -->
+ <skip />
+ <!-- no translation found for shutdown_progress (5464239146561542178) -->
+ <skip />
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Bona izinyathelo zokunakekelwa"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Bona izinyathelo zokunakekelwa"</string>
<string name="high_temp_alarm_title" msgid="8654754369605452169">"Khipha idivayisi yakho"</string>
@@ -1186,6 +1195,7 @@
<string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
<string name="note_task_button_label" msgid="230135078402003532">"Ukuthatha amanothi"</string>
<string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ukuthatha amanothi, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
+ <string name="audio_sharing_description" msgid="8849060142768870004">"Yabelana ngomsindo"</string>
<string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Ukusakaza"</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Misa ukusakaza i-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Uma usakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g> noma ushintsha okuphumayo, ukusakaza kwakho kwamanje kuzoma"</string>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index e35840b85ab4..8c7b652cdfe0 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -126,6 +126,9 @@
<item msgid="8259411607272330225">"Valiwe"</item>
<item msgid="578444932039713369">"Vuliwe"</item>
</string-array>
+ <!-- no translation found for tile_states_record_issue:0 (1727196795383575383) -->
+ <!-- no translation found for tile_states_record_issue:1 (9061144428113385092) -->
+ <!-- no translation found for tile_states_record_issue:2 (2984256114867200368) -->
<string-array name="tile_states_reverse">
<item msgid="3574611556622963971">"Akutholakali"</item>
<item msgid="8707481475312432575">"Valiwe"</item>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index beaa708b4dcc..e181d079fc6d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -101,7 +101,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 346bdfc8b2d8..b71341791ee6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3254,6 +3254,12 @@
<!-- Text for education page content description for unfolded animation. [CHAR_LIMIT=NONE] -->
<string name="rear_display_accessibility_unfolded_animation">Foldable device being flipped around</string>
+ <!-- QuickSettings: Additional label for the auto-rotation quicksettings tile indicating that the setting corresponds to the folded posture for a foldable device [CHAR LIMIT=32] -->
+ <string name="quick_settings_rotation_posture_folded">folded</string>
+ <!-- QuickSettings: Additional label for the auto-rotation quicksettings tile indicating that the setting corresponds to the unfolded posture for a foldable device [CHAR LIMIT=32] -->
+ <string name="quick_settings_rotation_posture_unfolded">unfolded</string>
+ <!-- QuickSettings: template for rotation tile foldable secondary label [CHAR LIMIT=64] !-->
+ <string name="rotation_tile_with_posture_secondary_label_template">%1$s / %2$s</string>
<!-- Title for notification of low stylus battery with percentage. "percentage" is
the value of the battery capacity remaining [CHAR LIMIT=none]-->
<string name="stylus_battery_low_percentage"><xliff:g id="percentage" example="16%">%s</xliff:g> battery remaining</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 617eadbd447c..ce08ca3e43af 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1581,4 +1581,8 @@
<style name="Theme.PrivacyDialog" parent="@style/Theme.SystemUI.Dialog">
<item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainer</item>
</style>
+
+ <style name="Theme.SystemUI.Dialog.StickyKeys" parent="@style/Theme.SystemUI.Dialog">
+ <item name="android:colorBackground">@color/transparent</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index 7020d548f6b0..9036a35846af 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -222,6 +222,16 @@
<item>On</item>
</string-array>
+ <!-- State names for record_issue tile: unavailable, off, on.
+ This subtitle is shown when the tile is in that particular state but does not set its own
+ subtitle, so some of these may never appear on screen. They should still be translated as
+ if they could appear. [CHAR LIMIT=32] -->
+ <string-array name="tile_states_record_issue">
+ <item>Unavailable</item>
+ <item>Off</item>
+ <item>On</item>
+ </string-array>
+
<!-- State names for reverse (charging) tile: unavailable, off, on.
This subtitle is shown when the tile is in that particular state but does not set its own
subtitle, so some of these may never appear on screen. They should still be translated as
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index 75cace424e8b..b9b8fbe9637f 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -209,6 +209,9 @@ public class CarrierTextManager {
// This will set/remove the listeners appropriately. Note that it will never double
// add the listeners.
handleSetListening(mCarrierTextCallback);
+ mainExecutor.execute(() -> {
+ mKeyguardUpdateMonitor.registerCallback(mCallback);
+ });
}
});
}
@@ -276,7 +279,6 @@ public class CarrierTextManager {
if (mNetworkSupported.get()) {
// Keyguard update monitor expects callbacks from main thread
mMainExecutor.execute(() -> {
- mKeyguardUpdateMonitor.registerCallback(mCallback);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
});
mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener);
@@ -289,7 +291,6 @@ public class CarrierTextManager {
} else {
mCarrierTextCallback = null;
mMainExecutor.execute(() -> {
- mKeyguardUpdateMonitor.removeCallback(mCallback);
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
});
mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener);
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 169a4e0f3501..f28d4052b5a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,8 +15,6 @@
*/
package com.android.keyguard
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -43,14 +41,16 @@ import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags.REGION_SAMPLING
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.core.Logger
+import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockTickRate
-import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.plugins.clocks.ZenData.ZenMode
@@ -61,16 +61,18 @@ import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChang
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.concurrency.DelayableExecutor
+import java.util.Locale
+import java.util.TimeZone
+import java.util.concurrent.Executor
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
-import java.util.Locale
-import java.util.TimeZone
-import java.util.concurrent.Executor
-import javax.inject.Inject
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -93,11 +95,13 @@ constructor(
private val featureFlags: FeatureFlagsClassic,
private val zenModeController: ZenModeController,
) {
- var loggers = listOf(
- clockBuffers.infraMessageBuffer,
- clockBuffers.smallClockMessageBuffer,
- clockBuffers.largeClockMessageBuffer
- ).map { Logger(it, TAG) }
+ var loggers =
+ listOf(
+ clockBuffers.infraMessageBuffer,
+ clockBuffers.smallClockMessageBuffer,
+ clockBuffers.largeClockMessageBuffer
+ )
+ .map { Logger(it, TAG) }
var clock: ClockController? = null
get() = field
@@ -108,11 +112,12 @@ constructor(
}
private fun disconnectClock(clock: ClockController?) {
- if (clock == null) { return; }
+ if (clock == null) {
+ return
+ }
smallClockOnAttachStateChangeListener?.let {
clock.smallClock.view.removeOnAttachStateChangeListener(it)
- smallClockFrame?.viewTreeObserver
- ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
+ smallClockFrame?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
}
largeClockOnAttachStateChangeListener?.let {
clock.largeClock.view.removeOnAttachStateChangeListener(it)
@@ -120,7 +125,9 @@ constructor(
}
private fun connectClock(clock: ClockController?) {
- if (clock == null) { return; }
+ if (clock == null) {
+ return
+ }
val clockStr = clock.toString()
loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } }
@@ -129,23 +136,27 @@ constructor(
if (!regionSamplingEnabled) {
updateColors()
} else {
- smallRegionSampler = createRegionSampler(
- clock.smallClock.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- ).apply { startRegionSampler() }
-
- largeRegionSampler = createRegionSampler(
- clock.largeClock.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- ).apply { startRegionSampler() }
+ smallRegionSampler =
+ createRegionSampler(
+ clock.smallClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ )
+ .apply { startRegionSampler() }
+
+ largeRegionSampler =
+ createRegionSampler(
+ clock.largeClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ )
+ .apply { startRegionSampler() }
updateColors()
}
@@ -158,49 +169,49 @@ constructor(
}
clock.events.onWeatherDataChanged(it)
}
- zenData?.let {
- clock.events.onZenDataChanged(it)
- }
- alarmData?.let {
- clock.events.onAlarmDataChanged(it)
- }
-
- smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
- var pastVisibility: Int? = null
- override fun onViewAttachedToWindow(view: View) {
- clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- // Match the asing for view.parent's layout classes.
- smallClockFrame = (view.parent as ViewGroup)?.also { frame ->
- pastVisibility = frame.visibility
- onGlobalLayoutListener = OnGlobalLayoutListener {
- val currentVisibility = frame.visibility
- if (pastVisibility != currentVisibility) {
- pastVisibility = currentVisibility
- // when small clock is visible,
- // recalculate bounds and sample
- if (currentVisibility == View.VISIBLE) {
- smallRegionSampler?.stopRegionSampler()
- smallRegionSampler?.startRegionSampler()
+ zenData?.let { clock.events.onZenDataChanged(it) }
+ alarmData?.let { clock.events.onAlarmDataChanged(it) }
+
+ smallClockOnAttachStateChangeListener =
+ object : OnAttachStateChangeListener {
+ var pastVisibility: Int? = null
+ override fun onViewAttachedToWindow(view: View) {
+ clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ // Match the asing for view.parent's layout classes.
+ smallClockFrame =
+ (view.parent as ViewGroup)?.also { frame ->
+ pastVisibility = frame.visibility
+ onGlobalLayoutListener = OnGlobalLayoutListener {
+ val currentVisibility = frame.visibility
+ if (pastVisibility != currentVisibility) {
+ pastVisibility = currentVisibility
+ // when small clock is visible,
+ // recalculate bounds and sample
+ if (currentVisibility == View.VISIBLE) {
+ smallRegionSampler?.stopRegionSampler()
+ smallRegionSampler?.startRegionSampler()
+ }
+ }
}
+ frame.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
}
- }
- frame.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
}
- }
- override fun onViewDetachedFromWindow(p0: View) {
- smallClockFrame?.viewTreeObserver
+ override fun onViewDetachedFromWindow(p0: View) {
+ smallClockFrame
+ ?.viewTreeObserver
?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
+ }
}
- }
clock.smallClock.view.addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
- largeClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
- override fun onViewAttachedToWindow(p0: View) {
- clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ largeClockOnAttachStateChangeListener =
+ object : OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(p0: View) {
+ clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+ override fun onViewDetachedFromWindow(p0: View) {}
}
- override fun onViewDetachedFromWindow(p0: View) {}
- }
clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
}
@@ -263,7 +274,9 @@ constructor(
bgExecutor,
regionSamplingEnabled,
isLockscreen,
- ) { updateColors() }
+ ) {
+ updateColors()
+ }
}
var smallRegionSampler: RegionSampler? = null
@@ -364,35 +377,36 @@ constructor(
}
}
- private val zenModeCallback = object : ZenModeController.Callback {
- override fun onZenChanged(zen: Int) {
- var mode = ZenMode.fromInt(zen)
- if (mode == null) {
- Log.e(TAG, "Failed to get zen mode from int: $zen")
- return
- }
-
- zenData = ZenData(
- mode,
- if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
- else SysuiR.string::dnd_is_on.name
- ).also { data ->
- mainExecutor.execute {
- clock?.run { events.onZenDataChanged(data) }
+ private val zenModeCallback =
+ object : ZenModeController.Callback {
+ override fun onZenChanged(zen: Int) {
+ var mode = ZenMode.fromInt(zen)
+ if (mode == null) {
+ Log.e(TAG, "Failed to get zen mode from int: $zen")
+ return
}
+
+ zenData =
+ ZenData(
+ mode,
+ if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
+ else SysuiR.string::dnd_is_on.name
+ )
+ .also { data ->
+ mainExecutor.execute { clock?.run { events.onZenDataChanged(data) } }
+ }
}
- }
- override fun onNextAlarmChanged() {
- val nextAlarmMillis = zenModeController.getNextAlarm()
- alarmData = AlarmData(
- if (nextAlarmMillis > 0) nextAlarmMillis else null,
- SysuiR.string::status_bar_alarm.name
- ).also { data ->
- clock?.run { events.onAlarmDataChanged(data) }
+ override fun onNextAlarmChanged() {
+ val nextAlarmMillis = zenModeController.getNextAlarm()
+ alarmData =
+ AlarmData(
+ if (nextAlarmMillis > 0) nextAlarmMillis else null,
+ SysuiR.string::status_bar_alarm.name
+ )
+ .also { data -> clock?.run { events.onAlarmDataChanged(data) } }
}
}
- }
fun registerListeners(parent: View) {
if (isRegistered) {
@@ -413,6 +427,7 @@ constructor(
listenForDozing(this)
if (migrateClocksToBlueprint()) {
listenForDozeAmountTransition(this)
+ listenForAnyStateToAodTransition(this)
} else {
listenForDozeAmount(this)
}
@@ -444,12 +459,15 @@ constructor(
largeRegionSampler?.stopRegionSampler()
smallTimeListener?.stop()
largeTimeListener?.stop()
- clock?.smallClock?.view
- ?.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
- smallClockFrame?.viewTreeObserver
- ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
- clock?.largeClock?.view
- ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
+ clock
+ ?.smallClock
+ ?.view
+ ?.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+ smallClockFrame?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
+ clock
+ ?.largeClock
+ ?.view
+ ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
}
/**
@@ -473,12 +491,10 @@ constructor(
largeTimeListener = null
clock?.let {
- smallTimeListener = TimeListener(it.smallClock, mainExecutor).apply {
- update(shouldTimeListenerRun)
- }
- largeTimeListener = TimeListener(it.largeClock, mainExecutor).apply {
- update(shouldTimeListenerRun)
- }
+ smallTimeListener =
+ TimeListener(it.smallClock, mainExecutor).apply { update(shouldTimeListenerRun) }
+ largeTimeListener =
+ TimeListener(it.largeClock, mainExecutor).apply { update(shouldTimeListenerRun) }
}
}
@@ -517,7 +533,27 @@ constructor(
@VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.dozeAmountTransition.collect { handleDoze(it.value) }
+ merge(
+ keyguardTransitionInteractor.aodToLockscreenTransition.map { step ->
+ step.copy(value = 1f - step.value)
+ },
+ keyguardTransitionInteractor.lockscreenToAodTransition,
+ )
+ .collect { handleDoze(it.value) }
+ }
+ }
+
+ /**
+ * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
+ */
+ @VisibleForTesting
+ internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
+ return scope.launch {
+ keyguardTransitionInteractor
+ .transitionStepsToState(AOD)
+ .filter { it.transitionState == TransitionState.STARTED }
+ .filter { it.from != LOCKSCREEN }
+ .collect { handleDoze(1f) }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index fc4e122d235b..4c3f623595bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -103,7 +103,7 @@ class KeyguardEsimArea extends Button implements View.OnClickListener {
public static boolean isEsimLocked(Context context, int subId) {
EuiccManager euiccManager =
(EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
- if (!euiccManager.isEnabled()) {
+ if (euiccManager == null || !euiccManager.isEnabled()) {
return false;
}
SubscriptionInfo sub = SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
@@ -118,6 +118,10 @@ class KeyguardEsimArea extends Button implements View.OnClickListener {
@Override
public void onClick(View v) {
+ if (mEuiccManager == null) {
+ Log.e(TAG, "EuiccManager not present");
+ return;
+ }
SubscriptionInfo sub = SubscriptionManager.from(mContext)
.getActiveSubscriptionInfo(mSubscriptionId);
if (sub == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 363dd014beb6..f528ec8af134 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -18,11 +18,16 @@ package com.android.keyguard;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.SystemClock;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.util.Log;
+import android.util.Pair;
import android.view.View;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -40,6 +45,16 @@ import javax.inject.Inject;
public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
extends ViewController<T> {
/**
+ * Pair representing:
+ * first - BiometricSource the currently displayed message is associated with.
+ * second - Timestamp the biometric message came in uptimeMillis.
+ * This Pair can be null if the message is not associated with a biometric.
+ */
+ @Nullable
+ private Pair<BiometricSourceType, Long> mMessageBiometricSource = null;
+ private static final Long SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS = 3500L;
+
+ /**
* Delay before speaking an accessibility announcement. Used to prevent
* lift-to-type from interrupting itself.
*/
@@ -149,12 +164,42 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
* Sets a message to the underlying text view.
*/
public void setMessage(CharSequence s, boolean animate) {
+ setMessage(s, animate, null);
+ }
+
+ /**
+ * Sets a message to the underlying text view.
+ */
+ public void setMessage(CharSequence s, BiometricSourceType biometricSourceType) {
+ setMessage(s, true, biometricSourceType);
+ }
+
+ private void setMessage(
+ CharSequence s,
+ boolean animate,
+ BiometricSourceType biometricSourceType) {
+ final long uptimeMillis = SystemClock.uptimeMillis();
+ if (skipShowingFaceMessage(biometricSourceType, uptimeMillis)) {
+ Log.d("KeyguardMessageAreaController", "Skip showing face message \"" + s + "\"");
+ return;
+ }
+ mMessageBiometricSource = new Pair<>(biometricSourceType, uptimeMillis);
if (mView.isDisabled()) {
return;
}
mView.setMessage(s, animate);
}
+ private boolean skipShowingFaceMessage(
+ BiometricSourceType biometricSourceType, Long currentUptimeMillis
+ ) {
+ return mMessageBiometricSource != null
+ && biometricSourceType == BiometricSourceType.FACE
+ && mMessageBiometricSource.first == BiometricSourceType.FINGERPRINT
+ && (currentUptimeMillis - mMessageBiometricSource.second)
+ < SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS;
+ }
+
public void setMessage(int resId) {
String message = resId != 0 ? mView.getResources().getString(resId) : null;
setMessage(message);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 7473e0c62d1d..490ad5c4136d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -86,8 +86,9 @@ public class KeyguardPasswordViewController
};
private final View.OnKeyListener mKeyListener = (v, keyCode, keyEvent) -> {
+ // Ignore SPACE as a confirm key to allow the space character within passwords.
final boolean isKeyboardEnterKey = keyEvent != null
- && KeyEvent.isConfirmKey(keyCode)
+ && KeyEvent.isConfirmKey(keyCode) && keyCode != KeyEvent.KEYCODE_SPACE
&& keyEvent.getAction() == KeyEvent.ACTION_UP;
if (isKeyboardEnterKey) {
verifyPasswordAndUnlock();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 38c2829e27f6..8c51a4e0ce66 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -34,6 +34,7 @@ import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -437,7 +438,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
- private final OnSubscriptionsChangedListener mSubscriptionListener =
+ @VisibleForTesting
+ final OnSubscriptionsChangedListener mSubscriptionListener =
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
@@ -586,16 +588,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void handleSimSubscriptionInfoChanged() {
Assert.isMainThread();
mLogger.v("onSubscriptionInfoChanged()");
- List<SubscriptionInfo> sil = mSubscriptionManager
- .getCompleteActiveSubscriptionInfoList();
- if (sil != null) {
- for (SubscriptionInfo subInfo : sil) {
+ List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
+ if (!subscriptionInfos.isEmpty()) {
+ for (SubscriptionInfo subInfo : subscriptionInfos) {
mLogger.logSubInfo(subInfo);
}
} else {
mLogger.v("onSubscriptionInfoChanged: list is null");
}
- List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
// Hack level over 9000: Because the subscription id is not yet valid when we see the
// first update in handleSimStateChange, we need to force refresh all SIM states
@@ -658,18 +658,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/**
* @return List of SubscriptionInfo records, maybe empty but never null.
+ *
+ * Note that this method will filter out any subscription which is PROFILE_CLASS_PROVISIONING
*/
public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
List<SubscriptionInfo> sil = mSubscriptionInfo;
if (sil == null || forceReload) {
- sil = mSubscriptionManager.getCompleteActiveSubscriptionInfoList();
- }
- if (sil == null) {
- // getCompleteActiveSubscriptionInfoList was null callers expect an empty list.
- mSubscriptionInfo = new ArrayList<>();
- } else {
- mSubscriptionInfo = sil;
+ mSubscriptionInfo = mSubscriptionManager.getCompleteActiveSubscriptionInfoList()
+ .stream()
+ .filter(subInfo -> subInfo.getProfileClass() != PROFILE_CLASS_PROVISIONING)
+ .toList();
}
+
return new ArrayList<>(mSubscriptionInfo);
}
@@ -3313,6 +3313,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
becameAbsent |= ABSENT_SIM_STATE_LIST.contains(state);
+ // TODO(b/327476182): Preserve SIM_STATE_CARD_IO_ERROR sims in a separate data source.
SimData data = mSimDatas.get(subId);
final boolean changed;
if (data == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index d2ad096c1207..ce4032aaea05 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,6 +16,7 @@
package com.android.keyguard.logging
+import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
import com.android.systemui.log.LogBuffer
@@ -117,6 +118,26 @@ constructor(
)
}
+ fun logDropNonFingerprintMessage(
+ message: CharSequence,
+ followUpMessage: CharSequence?,
+ biometricSourceType: BiometricSourceType?,
+ ) {
+ buffer.log(
+ KeyguardIndicationController.TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = message.toString()
+ str2 = followUpMessage?.toString()
+ str3 = biometricSourceType?.name
+ },
+ {
+ "droppingNonFingerprintMessage message=$str1 " +
+ "followUpMessage:$str2 biometricSourceType:$str3"
+ }
+ )
+ }
+
fun logUpdateBatteryIndication(
powerIndication: String,
pluggedIn: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 88535893b60c..33f14d44de75 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -22,17 +22,13 @@ import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.Preconditions;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
-import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
@@ -47,7 +43,6 @@ import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -58,7 +53,6 @@ import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
@@ -70,6 +64,7 @@ import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
+
/**
* Class to handle ugly dependencies throughout sysui until we determine the
* long-term dependency injection solution.
@@ -96,10 +91,6 @@ public class Dependency {
* Key for getting a Handler for receiving time tick broadcasts on.
*/
public static final String TIME_TICK_HANDLER_NAME = "time_tick_handler";
- /**
- * Generic handler on the main thread.
- */
- private static final String MAIN_HANDLER_NAME = "main_handler";
/**
* An email address to send memory leak reports to by default.
@@ -121,11 +112,6 @@ public class Dependency {
*/
public static final DependencyKey<Handler> TIME_TICK_HANDLER =
new DependencyKey<>(TIME_TICK_HANDLER_NAME);
- /**
- * Generic handler on the main thread.
- */
- public static final DependencyKey<Handler> MAIN_HANDLER =
- new DependencyKey<>(MAIN_HANDLER_NAME);
private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
private final ArrayMap<Object, LazyDependencyCreator> mProviders = new ArrayMap<>();
@@ -134,7 +120,6 @@ public class Dependency {
@Inject Lazy<BroadcastDispatcher> mBroadcastDispatcher;
@Inject Lazy<BluetoothController> mBluetoothController;
- @Inject Lazy<FlashlightController> mFlashlightController;
@Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor;
@Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
@Inject Lazy<PluginManager> mPluginManager;
@@ -150,15 +135,10 @@ public class Dependency {
@Inject Lazy<LightBarController> mLightBarController;
@Inject Lazy<OverviewProxyService> mOverviewProxyService;
@Inject Lazy<NavigationModeController> mNavBarModeController;
- @Inject Lazy<AccessibilityButtonModeObserver> mAccessibilityButtonModeObserver;
- @Inject Lazy<AccessibilityButtonTargetsObserver> mAccessibilityButtonListController;
- @Inject Lazy<IStatusBarService> mIStatusBarService;
- @Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
@Inject Lazy<NavigationBarController> mNavigationBarController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
@Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
@Inject @Background Lazy<Looper> mBgLooper;
- @Inject @Main Lazy<Handler> mMainHandler;
@Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
@Inject Lazy<CommandQueue> mCommandQueue;
@@ -187,10 +167,8 @@ public class Dependency {
// on imports.
mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
mProviders.put(BG_LOOPER, mBgLooper::get);
- mProviders.put(MAIN_HANDLER, mMainHandler::get);
mProviders.put(BroadcastDispatcher.class, mBroadcastDispatcher::get);
mProviders.put(BluetoothController.class, mBluetoothController::get);
- mProviders.put(FlashlightController.class, mFlashlightController::get);
mProviders.put(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor::get);
mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
mProviders.put(PluginManager.class, mPluginManager::get);
@@ -205,13 +183,6 @@ public class Dependency {
mProviders.put(LightBarController.class, mLightBarController::get);
mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
mProviders.put(NavigationModeController.class, mNavBarModeController::get);
- mProviders.put(AccessibilityButtonModeObserver.class,
- mAccessibilityButtonModeObserver::get);
- mProviders.put(AccessibilityButtonTargetsObserver.class,
- mAccessibilityButtonListController::get);
- mProviders.put(IStatusBarService.class, mIStatusBarService::get);
- mProviders.put(NotificationRemoteInputManager.Callback.class,
- mNotificationRemoteInputManagerCallback::get);
mProviders.put(NavigationBarController.class, mNavigationBarController::get);
mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 0bd44f0f3901..9de71c1880fe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,7 +17,7 @@
package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.Flags.customBiometricPrompt;
+import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
@@ -33,6 +33,7 @@ import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -43,6 +44,7 @@ import android.os.Looper;
import android.os.UserManager;
import android.util.Log;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -160,6 +162,8 @@ public class AuthContainerView extends LinearLayout
private final ImageView mBackgroundView;
private final ScrollView mBiometricScrollView;
private final View mPanelView;
+ private final List<FingerprintSensorPropertiesInternal> mFpProps;
+ private final List<FaceSensorPropertiesInternal> mFaceProps;
private final float mTranslationY;
@VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
private final Set<Integer> mFailedModalities = new HashSet<Integer>();
@@ -374,6 +378,8 @@ public class AuthContainerView extends LinearLayout
mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
mPromptViewModel = promptViewModel;
+ mFpProps = fpProps;
+ mFaceProps = faceProps;
showPrompt(config, layoutInflater, promptViewModel,
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
@@ -402,7 +408,12 @@ public class AuthContainerView extends LinearLayout
@Nullable FaceSensorPropertiesInternal faceProps,
@NonNull VibratorHelper vibratorHelper
) {
- if (Utils.isBiometricAllowed(config.mPromptInfo) || customBiometricPrompt()) {
+ // Set this value before showing either of the prompt.
+ mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
+ config.mPromptInfo);
+
+ if (Utils.isBiometricAllowed(config.mPromptInfo)
+ || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
} else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true, false);
@@ -411,7 +422,6 @@ public class AuthContainerView extends LinearLayout
}
}
-
private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
@NonNull PromptViewModel viewModel,
@Nullable FingerprintSensorPropertiesInternal fpProps,
@@ -517,7 +527,7 @@ public class AuthContainerView extends LinearLayout
@Override
public void onOrientationChanged() {
if (!constraintBp()) {
- maybeUpdatePositionForUdfps(true /* invalidate */);
+ updatePositionByCapability(true /* invalidate */);
}
}
@@ -534,7 +544,8 @@ public class AuthContainerView extends LinearLayout
() -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
if (constraintBp()) {
// Do nothing on attachment with constraintLayout
- } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo) || customBiometricPrompt()) {
+ } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)
+ || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
mBiometricScrollView.addView(mBiometricView.asView());
} else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true /* animatePanel */, false /* animateContents */);
@@ -544,7 +555,7 @@ public class AuthContainerView extends LinearLayout
}
if (!constraintBp()) {
- maybeUpdatePositionForUdfps(false /* invalidate */);
+ updatePositionByCapability(false /* invalidate */);
}
if (mConfig.mSkipIntro) {
@@ -610,6 +621,22 @@ public class AuthContainerView extends LinearLayout
};
}
+ private void updatePositionByCapability(boolean forceInvalidate) {
+ final FingerprintSensorPropertiesInternal fpProp = Utils.findFirstSensorProperties(
+ mFpProps, mConfig.mSensorIds);
+ final FaceSensorPropertiesInternal faceProp = Utils.findFirstSensorProperties(
+ mFaceProps, mConfig.mSensorIds);
+ if (fpProp != null && fpProp.isAnyUdfpsType()) {
+ maybeUpdatePositionForUdfps(forceInvalidate /* invalidate */);
+ }
+ if (faceProp != null && mBiometricView.isFaceOnly()) {
+ alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */);
+ }
+ if (fpProp != null && fpProp.sensorType == TYPE_POWER_BUTTON) {
+ alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */);
+ }
+ }
+
private static boolean shouldUpdatePositionForUdfps(@NonNull View view) {
if (view instanceof BiometricPromptLayout) {
// this will force the prompt to align itself on the edge of the screen
@@ -627,11 +654,14 @@ public class AuthContainerView extends LinearLayout
if (display == null) {
return false;
}
+
+ final DisplayInfo cachedDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(cachedDisplayInfo);
if (mBiometricView == null || !shouldUpdatePositionForUdfps(mBiometricView.asView())) {
return false;
}
- final int displayRotation = display.getRotation();
+ final int displayRotation = cachedDisplayInfo.rotation;
switch (displayRotation) {
case Surface.ROTATION_0:
mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
@@ -663,6 +693,38 @@ public class AuthContainerView extends LinearLayout
return true;
}
+ private boolean alwaysUpdatePositionAtScreenBottom(boolean invalidate) {
+ final Display display = getDisplay();
+ if (display == null) {
+ return false;
+ }
+ if (mBiometricView == null || !shouldUpdatePositionForUdfps(mBiometricView.asView())) {
+ return false;
+ }
+
+ final int displayRotation = display.getRotation();
+ switch (displayRotation) {
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ case Surface.ROTATION_180:
+ mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
+ setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+ break;
+ default:
+ Log.e(TAG, "Unsupported display rotation: " + displayRotation);
+ mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
+ setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+ break;
+ }
+
+ if (invalidate) {
+ mPanelView.invalidateOutline();
+ }
+
+ return true;
+ }
+
private void setScrollViewGravity(int gravity) {
final FrameLayout.LayoutParams params =
(FrameLayout.LayoutParams) mBiometricScrollView.getLayoutParams();
@@ -819,6 +881,9 @@ public class AuthContainerView extends LinearLayout
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
+ if (Flags.customBiometricPrompt()) {
+ mPromptSelectorInteractorProvider.get().resetPrompt();
+ }
removeWindowIfAttached();
};
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index c8fb0449279f..8e5a97bd5d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.data.repository
import android.content.Context
+import android.util.DisplayMetrics
import android.util.Size
import android.view.DisplayInfo
import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -29,8 +30,10 @@ import com.android.systemui.display.data.repository.DeviceStateRepository.Device
import com.android.systemui.display.data.repository.DisplayRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -53,6 +56,9 @@ interface DisplayStateRepository {
/** Provides the current display size */
val currentDisplaySize: StateFlow<Size>
+
+ /** Provides whether the current display is large screen */
+ val isLargeScreen: Flow<Boolean>
}
@SysUISingleton
@@ -121,6 +127,15 @@ constructor(
),
)
+ override val isLargeScreen: Flow<Boolean> =
+ currentDisplayInfo
+ .map {
+ // TODO: This works, but investigate better way to handle this
+ it.logicalWidth * 160 / it.logicalDensityDpi > DisplayMetrics.DENSITY_XXXHIGH &&
+ it.logicalHeight * 160 / it.logicalDensityDpi > DisplayMetrics.DENSITY_XXHIGH
+ }
+ .distinctUntilChanged()
+
companion object {
const val TAG = "DisplayStateRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index ad7bb0e61178..b87fadf995e6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -16,8 +16,11 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.Flags
import android.hardware.biometrics.PromptInfo
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -65,6 +68,18 @@ interface PromptRepository {
*/
val isConfirmationRequired: Flow<Boolean>
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+ /**
+ * Update whether biometric prompt without icon needs to show for displaying content prior to
+ * credential view, which should be set before [setPrompt].
+ */
+ fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
/** Update the prompt configuration, which should be set before [isShowing]. */
fun setPrompt(
promptInfo: PromptInfo,
@@ -129,6 +144,19 @@ constructor(
}
.distinctUntilChanged()
+ private val _showBpWithoutIconForCredential: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+ val showBpForCredential =
+ Flags.customBiometricPrompt() &&
+ !Utils.isBiometricAllowed(promptInfo) &&
+ isDeviceCredentialAllowed(promptInfo) &&
+ promptInfo.contentView != null
+ _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+ }
+
override fun setPrompt(
promptInfo: PromptInfo,
userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
index 427361d4b17a..7d6721903c37 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
@@ -68,6 +68,8 @@ interface DisplayStateInteractor {
/** Called on configuration changes, used to keep the display state in sync */
fun onConfigurationChanged(newConfig: Configuration)
+
+ val isLargeScreen: Flow<Boolean>
}
/** Encapsulates logic for interacting with the display state. */
@@ -138,6 +140,8 @@ constructor(
.sample(defaultDisplay)
.map { it?.state == Display.STATE_OFF }
+ override val isLargeScreen: Flow<Boolean> = displayStateRepository.isLargeScreen
+
companion object {
private const val TAG = "DisplayStateInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index e3facff9af12..94cea5702fe3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -30,6 +30,7 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -59,6 +60,13 @@ constructor(
/** If the prompt is currently showing. */
val isShowing: Flow<Boolean> = biometricPromptRepository.isShowing
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean> =
+ biometricPromptRepository.showBpWithoutIconForCredential
+
/** Metadata about the current credential prompt, including app-supplied preferences. */
val prompt: Flow<BiometricPromptRequest.Credential?> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index b3f95748ccdc..45816c12281e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -32,6 +32,7 @@ import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -70,6 +71,18 @@ interface PromptSelectorInteractor {
/** Fingerprint sensor type */
val sensorType: Flow<FingerprintSensorType>
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+ /**
+ * Update whether biometric prompt without icon needs to show for displaying content prior to
+ * credential view, which should be set before [PromptRepository.setPrompt].
+ */
+ fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
/** Use biometrics for authentication. */
fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
@@ -154,6 +167,12 @@ constructor(
override val sensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType
+ override val showBpWithoutIconForCredential = promptRepository.showBpWithoutIconForCredential
+
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ promptRepository.setShouldShowBpWithoutIconForCredential(promptInfo)
+ }
+
override fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 6133a51c6497..6f079e2d3b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -17,6 +17,7 @@ sealed class BiometricPromptRequest(
val title: String,
val subtitle: String,
val description: String,
+ val contentView: PromptContentView?,
val userInfo: BiometricUserInfo,
val operationInfo: BiometricOperationInfo,
val showEmergencyCallButton: Boolean,
@@ -33,11 +34,11 @@ sealed class BiometricPromptRequest(
title = info.title?.toString() ?: "",
subtitle = info.subtitle?.toString() ?: "",
description = info.description?.toString() ?: "",
+ contentView = info.contentView,
userInfo = userInfo,
operationInfo = operationInfo,
showEmergencyCallButton = info.isShowEmergencyCallButton
) {
- val contentView: PromptContentView? = info.contentView
val logoRes: Int = info.logoRes
val logoBitmap: Bitmap? = info.logoBitmap
val logoDescription: String? = info.logoDescription
@@ -54,6 +55,7 @@ sealed class BiometricPromptRequest(
title = (info.deviceCredentialTitle ?: info.title)?.toString() ?: "",
subtitle = (info.deviceCredentialSubtitle ?: info.subtitle)?.toString() ?: "",
description = (info.deviceCredentialDescription ?: info.description)?.toString() ?: "",
+ contentView = info.contentView,
userInfo = userInfo,
operationInfo = operationInfo,
showEmergencyCallButton = info.isShowEmergencyCallButton
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
index 96582cb56dd7..e58c8ff92c03 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
@@ -31,7 +31,6 @@ import android.text.style.BulletSpan
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
-import android.widget.ScrollView
import android.widget.Space
import android.widget.TextView
import androidx.lifecycle.Lifecycle
@@ -46,7 +45,7 @@ import kotlinx.coroutines.launch
/** Sub-binder for [BiometricPromptLayout.customized_view_container]. */
object BiometricCustomizedViewBinder {
- fun bind(customizedViewContainer: ScrollView, spaceAbove: Space, viewModel: PromptViewModel) {
+ fun bind(customizedViewContainer: LinearLayout, spaceAbove: Space, viewModel: PromptViewModel) {
customizedViewContainer.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index cd5b12482d83..e48f05d09fc4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -22,7 +22,6 @@ import android.content.Context
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.face.FaceManager
import android.text.method.ScrollingMovementMethod
import android.util.Log
@@ -33,7 +32,7 @@ import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
import android.view.accessibility.AccessibilityManager
import android.widget.Button
import android.widget.ImageView
-import android.widget.ScrollView
+import android.widget.LinearLayout
import android.widget.TextView
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
@@ -101,7 +100,7 @@ object BiometricViewBinder {
val subtitleView = view.requireViewById<TextView>(R.id.subtitle)
val descriptionView = view.requireViewById<TextView>(R.id.description)
val customizedViewContainer =
- view.requireViewById<ScrollView>(R.id.customized_view_container)
+ view.requireViewById<LinearLayout>(R.id.customized_view_container)
// set selected to enable marquee unless a screen reader is enabled
logoView.isSelected =
@@ -119,7 +118,7 @@ object BiometricViewBinder {
val iconSizeOverride =
if (constraintBp()) {
- viewModel.fingerprintAffordanceSize
+ null
} else {
(view as BiometricPromptLayout).updatedFingerprintAffordanceSize
}
@@ -151,17 +150,6 @@ object BiometricViewBinder {
// these do not change and need to be set before any size transitions
val modalities = viewModel.modalities.first()
- // If there is no biometrics available, biometric prompt is showing just for displaying
- // content, no authentication needed.
- if (!(customBiometricPrompt() && modalities.isEmpty)) {
- PromptIconViewBinder.bind(
- iconView,
- iconOverlayView,
- iconSizeOverride,
- viewModel,
- )
- }
-
if (modalities.hasFingerprint) {
/**
* Load the given [rawResources] immediately so they are cached for use in the
@@ -233,6 +221,19 @@ object BiometricViewBinder {
)
}
+ lifecycleScope.launch {
+ viewModel.showBpWithoutIconForCredential.collect {
+ if (!it) {
+ PromptIconViewBinder.bind(
+ iconView,
+ iconOverlayView,
+ iconSizeOverride,
+ viewModel,
+ )
+ }
+ }
+ }
+
// TODO(b/251476085): migrate legacy icon controllers and remove
// The fingerprint sensor is started by the legacy
// AuthContainerView#onDialogAnimatedIn in all cases but the implicit coex flow
@@ -651,6 +652,8 @@ class Spaghetti(
fun isCoex() = modalities.hasFaceAndFingerprint
+ fun isFaceOnly() = modalities.hasFaceOnly
+
fun asView() = view
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index a37d9168dfd3..478ef8f296b2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -21,7 +21,6 @@ import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.graphics.Outline
import android.graphics.Rect
-import android.hardware.biometrics.Flags
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.view.Surface
@@ -55,6 +54,7 @@ import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
import com.android.systemui.biometrics.ui.viewmodel.isRight
import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.biometrics.ui.viewmodel.isTop
+import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import kotlin.math.abs
@@ -100,6 +100,8 @@ object BiometricViewSizeBinder {
val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline)
val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline)
val bottomGuideline = view.requireViewById<Guideline>(R.id.bottomGuideline)
+ val topGuideline = view.requireViewById<Guideline>(R.id.topGuideline)
+ val midGuideline = view.findViewById<Guideline>(R.id.midGuideline)
val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
val panelView = view.requireViewById<View>(R.id.panel)
@@ -111,18 +113,30 @@ object BiometricViewSizeBinder {
val smallConstraintSet = ConstraintSet()
smallConstraintSet.clone(mediumConstraintSet)
- viewsToHideWhenSmall.forEach { smallConstraintSet.setVisibility(it.id, View.GONE) }
val largeConstraintSet = ConstraintSet()
largeConstraintSet.clone(mediumConstraintSet)
- viewsToHideWhenSmall.forEach { largeConstraintSet.setVisibility(it.id, View.GONE) }
- largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
- largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
largeConstraintSet.setGuidelineBegin(leftGuideline.id, 0)
largeConstraintSet.setGuidelineEnd(rightGuideline.id, 0)
largeConstraintSet.setGuidelineEnd(bottomGuideline.id, 0)
+ // TODO: Investigate better way to handle 180 rotations
+ val flipConstraintSet = ConstraintSet()
+ flipConstraintSet.clone(mediumConstraintSet)
+ flipConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.START,
+ R.id.midGuideline,
+ ConstraintSet.START
+ )
+ flipConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.END,
+ R.id.rightGuideline,
+ ConstraintSet.END
+ )
+ flipConstraintSet.setHorizontalBias(R.id.biometric_icon, .2f)
+
// Round the panel outline
panelView.outlineProvider =
object : ViewOutlineProvider() {
@@ -138,70 +152,51 @@ object BiometricViewSizeBinder {
.getInsets(WindowInsets.Type.navigationBars())
.bottom
+ // TODO: Move to viewmodel
fun measureBounds(position: PromptPosition) {
- val width = min(windowBounds.height(), windowBounds.width())
+ val density = windowManager.currentWindowMetrics.density
+ val width = min((640 * density).toInt(), windowBounds.width())
var left = -1
- var top = -1
var right = -1
var bottom = -1
+ var mid = -1
when {
position.isTop -> {
left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
- top = viewModel.promptMargin
right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
bottom = iconHolderView.centerY() * 2 - iconHolderView.centerY() / 4
}
position.isBottom -> {
- if (view.isLandscape()) {
- left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
- top = iconHolderView.centerY()
- right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
- bottom = bottomInset + viewModel.promptMargin
- } else {
- left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
- top =
- windowBounds.height() -
- (windowBounds.height() - iconHolderView.centerY()) * 2 +
- viewModel.promptMargin
- right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
- bottom = viewModel.promptMargin
- }
+ left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+ right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+ bottom = viewModel.promptMargin
}
-
- // For Udfps exclusive left and right, measure guideline to center
- // icon in BP
position.isLeft -> {
left = viewModel.promptMargin
- top =
- windowBounds.height() -
- (windowBounds.height() - iconHolderView.centerY()) * 2 +
- viewModel.promptMargin
- right =
+ mid =
abs(
windowBounds.width() - iconHolderView.centerX() * 2 +
viewModel.promptMargin
)
+ right = windowBounds.width() - (windowBounds.width() * .85).toInt()
bottom = bottomInset + viewModel.promptMargin
}
position.isRight -> {
- left =
+ left = windowBounds.width() - (windowBounds.width() * .85).toInt()
+ right = viewModel.promptMargin
+ bottom = bottomInset + viewModel.promptMargin
+ mid =
abs(
iconHolderView.centerX() -
(windowBounds.width() - iconHolderView.centerX()) -
viewModel.promptMargin
)
- top =
- windowBounds.height() -
- (windowBounds.height() - iconHolderView.centerY()) * 2 +
- viewModel.promptMargin
- right = viewModel.promptMargin
- bottom = bottomInset + viewModel.promptMargin
}
}
- val bounds = Rect(left, top, right, bottom)
+ val bounds = Rect(left, mid, right, bottom)
if (bounds.shouldAdjustLeftGuideline()) {
leftGuideline.setGuidelineBegin(bounds.left)
smallConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
@@ -217,15 +212,32 @@ object BiometricViewSizeBinder {
smallConstraintSet.setGuidelineEnd(bottomGuideline.id, bounds.bottom)
mediumConstraintSet.setGuidelineEnd(bottomGuideline.id, bounds.bottom)
}
+
+ if (position.isBottom) {
+ topGuideline.setGuidelinePercent(.25f)
+ mediumConstraintSet.setGuidelinePercent(topGuideline.id, .25f)
+ } else {
+ topGuideline.setGuidelinePercent(0f)
+ mediumConstraintSet.setGuidelinePercent(topGuideline.id, 0f)
+ }
+
+ if (mid != -1 && midGuideline != null) {
+ midGuideline.setGuidelineBegin(mid)
+ }
}
- view.repeatWhenAttached {
- var currentSize: PromptSize? = null
- val modalities = viewModel.modalities.first()
- // TODO(b/288175072): Move all visibility settings together.
- // If there is no biometrics available, biometric prompt is showing just for
- // displaying content, no authentication needed.
- if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+ fun setConstraintSetVisibility() {
+ viewsToHideWhenSmall.forEach {
+ mediumConstraintSet.setVisibility(it.id, it.showContentOrHide())
+ largeConstraintSet.setVisibility(it.id, View.GONE)
+ smallConstraintSet.setVisibility(it.id, View.GONE)
+ }
+
+ largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+
+ if (viewModel.showBpWithoutIconForCredential.value) {
smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
@@ -233,12 +245,23 @@ object BiometricViewSizeBinder {
mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
}
+ }
+
+ view.repeatWhenAttached {
+ var currentSize: PromptSize? = null
+
lifecycleScope.launch {
combine(viewModel.position, viewModel.size, ::Pair).collect {
(position, size) ->
view.doOnAttach {
- measureBounds(position)
+ if (position.isLeft) {
+ flipConstraintSet.applyTo(view)
+ } else if (position.isRight) {
+ mediumConstraintSet.applyTo(view)
+ }
+ measureBounds(position)
+ setConstraintSetVisibility()
when {
size.isSmall -> {
val ratio =
@@ -313,7 +336,6 @@ object BiometricViewSizeBinder {
// TODO(b/251476085): migrate the legacy panel controller and simplify this
view.repeatWhenAttached {
var currentSize: PromptSize? = null
- val modalities = viewModel.modalities.first()
lifecycleScope.launch {
/**
* View is only set visible in BiometricViewSizeBinder once PromptSize is
@@ -331,11 +353,13 @@ object BiometricViewSizeBinder {
// prepare for animated size transitions
for (v in viewsToHideWhenSmall) {
- v.showContentOrHide(forceHide = size.isSmall)
+ v.visibility = v.showContentOrHide(forceHide = size.isSmall)
}
- if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+
+ if (viewModel.showBpWithoutIconForCredential.value) {
iconHolderView.visibility = View.GONE
}
+
if (currentSize == null && size.isSmall) {
iconHolderView.alpha = 0f
}
@@ -344,9 +368,9 @@ object BiometricViewSizeBinder {
}
// TODO(b/302735104): Fix wrong height due to the delay of
- // PromptContentView. addOnLayoutChangeListener() will cause crash when
- // showing credential view, since |PromptIconViewModel| won't release
- // the flow.
+ // PromptContentView. addOnLayoutChangeListener() will cause crash
+ // when showing credential view, since |PromptIconViewModel| won't
+ // release the flow.
// propagate size changes to legacy panel controller and animate
// transitions
view.doOnLayout {
@@ -456,19 +480,24 @@ object BiometricViewSizeBinder {
}
private fun View.isLandscape(): Boolean {
- val r = context.display?.rotation
- return r == Surface.ROTATION_90 || r == Surface.ROTATION_270
+ val r = context.display.rotation
+ return if (
+ context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
+ ) {
+ r == Surface.ROTATION_0 || r == Surface.ROTATION_180
+ } else {
+ r == Surface.ROTATION_90 || r == Surface.ROTATION_270
+ }
}
-private fun View.showContentOrHide(forceHide: Boolean = false) {
+private fun View.showContentOrHide(forceHide: Boolean = false): Int {
val isTextViewWithBlankText = this is TextView && this.text.isBlank()
val isImageViewWithoutImage = this is ImageView && this.drawable == null
- visibility =
- if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
- View.GONE
- } else {
- View.VISIBLE
- }
+ return if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
+ View.GONE
+ } else {
+ View.VISIBLE
+ }
}
private fun View.centerX(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 2e47375b69fe..3469cfa210ba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -37,6 +37,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toQuint
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/** Sub-binder for [BiometricPromptLayout.iconView]. */
@@ -62,6 +63,12 @@ object PromptIconViewBinder {
iconOverlayView.layoutParams.width = iconViewLayoutParamSizeOverride.first
iconOverlayView.layoutParams.height = iconViewLayoutParamSizeOverride.second
+ } else {
+ iconView.layoutParams.width = viewModel.fingerprintIconWidth.first()
+ iconView.layoutParams.height = viewModel.fingerprintIconWidth.first()
+
+ iconOverlayView.layoutParams.width = viewModel.fingerprintIconWidth.first()
+ iconOverlayView.layoutParams.height = viewModel.fingerprintIconWidth.first()
}
var faceIcon: AnimatedVectorDrawable? = null
@@ -77,15 +84,12 @@ object PromptIconViewBinder {
}
launch {
- var width: Int
- var height: Int
+ var width = 0
+ var height = 0
viewModel.activeAuthType.collect { activeAuthType ->
when (activeAuthType) {
AuthType.Fingerprint,
AuthType.Coex -> {
- width = viewModel.fingerprintIconWidth
- height = viewModel.fingerprintIconHeight
-
/**
* View is only set visible in BiometricViewSizeBinder once
* PromptSize is determined that accounts for iconView size, to
@@ -113,7 +117,7 @@ object PromptIconViewBinder {
}
}
- if (iconViewLayoutParamSizeOverride == null) {
+ if (width != 0 && height != 0) {
iconView.layoutParams.width = width
iconView.layoutParams.height = height
iconOverlayView.layoutParams.width = width
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 03c5c5354ad7..46be8c74cee3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -4,13 +4,13 @@ import android.content.Context
import android.graphics.drawable.Drawable
import android.text.InputType
import com.android.internal.widget.LockPatternView
-import com.android.systemui.res.R
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.CredentialStatus
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
import com.android.systemui.biometrics.shared.model.BiometricUserInfo
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow
@@ -32,14 +32,16 @@ constructor(
/** Top level information about the prompt. */
val header: Flow<CredentialHeaderViewModel> =
- credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>().map {
- request ->
+ combine(
+ credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
+ credentialInteractor.showBpWithoutIconForCredential
+ ) { request, showBpWithoutIconForCredential ->
BiometricPromptHeaderViewModelImpl(
request,
user = request.userInfo,
title = request.title,
- subtitle = request.subtitle,
- description = request.description,
+ subtitle = if (showBpWithoutIconForCredential) "" else request.subtitle,
+ description = if (showBpWithoutIconForCredential) "" else request.description,
icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
showEmergencyCallButton = request.showEmergencyCallButton
)
@@ -145,7 +147,7 @@ constructor(
.createLaunchEmergencyDialerIntent(null)
.setFlags(
android.content.Intent.FLAG_ACTIVITY_NEW_TASK or
- android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
+ android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
)
context.startActivity(intent)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index b7cffaf2bcde..257eb4a60328 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -159,8 +159,8 @@ constructor(
val lastPulseLightToDark: Flow<Boolean> = _lastPulseLightToDark.asStateFlow()
/** Layout params for fingerprint iconView */
- val fingerprintIconWidth: Int = promptViewModel.fingerprintIconWidth
- val fingerprintIconHeight: Int = promptViewModel.fingerprintIconHeight
+ val fingerprintIconWidth: Flow<Int> = promptViewModel.fingerprintSensorDiameter
+ val fingerprintIconHeight: Flow<Int> = promptViewModel.fingerprintSensorDiameter
/** Layout params for face iconView */
val faceIconWidth: Int = promptViewModel.faceIconWidth
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index c933e0e31d40..61aeffe03b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -47,6 +47,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -84,14 +85,15 @@ constructor(
val faceIconHeight: Int =
context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
- val fingerprintSensorDiameter: Int =
- (udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds.width() *
- udfpsOverlayInteractor.udfpsOverlayParams.value.scaleFactor)
- .toInt()
- val fingerprintAffordanceSize: Pair<Int, Int>? =
- if (fingerprintSensorDiameter != 0)
- Pair(fingerprintSensorDiameter, fingerprintSensorDiameter)
- else null
+ val fingerprintSensorDiameter: Flow<Int> =
+ combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
+ ->
+ if (modalities.hasUdfps) {
+ overlayParams.sensorBounds.width()
+ } else {
+ fingerprintIconWidth
+ }
+ }
private val _accessibilityHint = MutableSharedFlow<String>()
@@ -123,6 +125,9 @@ constructor(
/** The kind of credential the user has. */
val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind
+ val showBpWithoutIconForCredential: StateFlow<Boolean> =
+ promptSelectorInteractor.showBpWithoutIconForCredential
+
/** The label to use for the cancel button. */
val negativeButtonText: Flow<String> =
promptSelectorInteractor.prompt.map { it?.negativeButtonText ?: "" }
@@ -153,12 +158,13 @@ constructor(
/** The current position of the prompt */
val position: Flow<PromptPosition> =
- combine(_forceLargeSize, modalities, displayStateInteractor.currentRotation) {
- forceLarge,
- modalities,
- rotation ->
+ combine(
+ _forceLargeSize,
+ displayStateInteractor.isLargeScreen,
+ displayStateInteractor.currentRotation
+ ) { forceLarge, isLargeScreen, rotation ->
when {
- forceLarge || !modalities.hasUdfps -> PromptPosition.Bottom
+ forceLarge || isLargeScreen -> PromptPosition.Bottom
rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right
rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left
rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top
@@ -340,22 +346,22 @@ constructor(
/** If the indicator (help, error) message should be shown. */
val isIndicatorMessageVisible: Flow<Boolean> =
combine(
- size,
- message,
- ) { size, message ->
- size.isNotSmall && message.message.isNotBlank()
- }
- .distinctUntilChanged()
+ size,
+ position,
+ message,
+ ) { size, _, message ->
+ size.isNotSmall && message.message.isNotBlank()
+ }
/** If the auth is pending confirmation and the confirm button should be shown. */
val isConfirmButtonVisible: Flow<Boolean> =
combine(
- size,
- isPendingConfirmation,
- ) { size, isPendingConfirmation ->
- size.isNotSmall && isPendingConfirmation
- }
- .distinctUntilChanged()
+ size,
+ position,
+ isPendingConfirmation,
+ ) { size, _, isPendingConfirmation ->
+ size.isNotSmall && isPendingConfirmation
+ }
/** If the icon can be used as a confirmation button. */
val isIconConfirmButton: Flow<Boolean> = size.map { it.isNotSmall }.distinctUntilChanged()
@@ -363,28 +369,25 @@ constructor(
/** If the negative button should be shown. */
val isNegativeButtonVisible: Flow<Boolean> =
combine(
- size,
- isAuthenticated,
- promptSelectorInteractor.isCredentialAllowed,
- ) { size, authState, credentialAllowed ->
- size.isNotSmall && authState.isNotAuthenticated && !credentialAllowed
- }
- .distinctUntilChanged()
+ size,
+ position,
+ isAuthenticated,
+ promptSelectorInteractor.isCredentialAllowed,
+ ) { size, _, authState, credentialAllowed ->
+ size.isNotSmall && authState.isNotAuthenticated && !credentialAllowed
+ }
/** If the cancel button should be shown (. */
val isCancelButtonVisible: Flow<Boolean> =
combine(
- size,
- isAuthenticated,
- isNegativeButtonVisible,
- isConfirmButtonVisible,
- ) { size, authState, showNegativeButton, showConfirmButton ->
- size.isNotSmall &&
- authState.isAuthenticated &&
- !showNegativeButton &&
- showConfirmButton
- }
- .distinctUntilChanged()
+ size,
+ position,
+ isAuthenticated,
+ isNegativeButtonVisible,
+ isConfirmButtonVisible,
+ ) { size, _, authState, showNegativeButton, showConfirmButton ->
+ size.isNotSmall && authState.isAuthenticated && !showNegativeButton && showConfirmButton
+ }
private val _canTryAgainNow = MutableStateFlow(false)
/**
@@ -393,35 +396,34 @@ constructor(
*/
val canTryAgainNow: Flow<Boolean> =
combine(
- _canTryAgainNow,
- size,
- isAuthenticated,
- isRetrySupported,
- ) { readyToTryAgain, size, authState, supportsRetry ->
- readyToTryAgain && size.isNotSmall && supportsRetry && authState.isNotAuthenticated
- }
- .distinctUntilChanged()
+ _canTryAgainNow,
+ size,
+ position,
+ isAuthenticated,
+ isRetrySupported,
+ ) { readyToTryAgain, size, _, authState, supportsRetry ->
+ readyToTryAgain && size.isNotSmall && supportsRetry && authState.isNotAuthenticated
+ }
/** If the try again button show be shown (only the button, see [canTryAgainNow]). */
val isTryAgainButtonVisible: Flow<Boolean> =
combine(
- canTryAgainNow,
- modalities,
- ) { tryAgainIsPossible, modalities ->
- tryAgainIsPossible && modalities.hasFaceOnly
- }
- .distinctUntilChanged()
+ canTryAgainNow,
+ modalities,
+ ) { tryAgainIsPossible, modalities ->
+ tryAgainIsPossible && modalities.hasFaceOnly
+ }
/** If the credential fallback button show be shown. */
val isCredentialButtonVisible: Flow<Boolean> =
combine(
- size,
- isAuthenticated,
- promptSelectorInteractor.isCredentialAllowed,
- ) { size, authState, credentialAllowed ->
- size.isNotSmall && authState.isNotAuthenticated && credentialAllowed
- }
- .distinctUntilChanged()
+ size,
+ position,
+ isAuthenticated,
+ promptSelectorInteractor.isCredentialAllowed,
+ ) { size, _, authState, credentialAllowed ->
+ size.isNotSmall && authState.isNotAuthenticated && credentialAllowed
+ }
private val history = PromptHistoryImpl()
private var messageJob: Job? = null
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
index 269878b43dab..b9e1c55fbade 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
@@ -92,7 +92,7 @@ constructor(
keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val subscriptionManager: SubscriptionManagerProxy,
broadcastDispatcher: BroadcastDispatcher,
- euiccManager: EuiccManager,
+ euiccManager: EuiccManager?,
) : SimBouncerRepository {
private val isPukScreenAvailable: Boolean =
resources.getBoolean(com.android.internal.R.bool.config_enable_puk_unlock_screen)
@@ -163,7 +163,9 @@ constructor(
@SuppressLint("MissingPermission")
override val isLockedEsim: StateFlow<Boolean?> =
activeSubscriptionInfo
- .map { info -> info?.let { euiccManager.isEnabled && info.isEmbedded } }
+ .map { info ->
+ info?.let { euiccManager != null && euiccManager.isEnabled && info.isEmbedded }
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
index efa77926a423..0f61eef7cb96 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
@@ -50,7 +50,7 @@ object BouncerInteractorModule {
}
@Provides
- fun provideEuiccManager(@Application applicationContext: Context): EuiccManager {
- return applicationContext.getSystemService(Context.EUICC_SERVICE) as EuiccManager
+ fun provideEuiccManager(@Application applicationContext: Context): EuiccManager? {
+ return applicationContext.getSystemService(Context.EUICC_SERVICE) as EuiccManager?
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
index 99d1f1370f4f..c3d4cb30e700 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
@@ -61,7 +61,7 @@ constructor(
private val telephonyManager: TelephonyManager,
@Main private val resources: Resources,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val euiccManager: EuiccManager,
+ private val euiccManager: EuiccManager?,
// TODO(b/307977401): Replace this with `MobileConnectionsInteractor` when available.
mobileConnectionsRepository: MobileConnectionsRepository,
) {
@@ -141,11 +141,13 @@ constructor(
UserHandle.SYSTEM
)
applicationScope.launch(backgroundDispatcher) {
- euiccManager.switchToSubscription(
- INVALID_SUBSCRIPTION_ID,
- activeSubscription.portIndex,
- callbackIntent,
- )
+ if (euiccManager != null) {
+ euiccManager.switchToSubscription(
+ INVALID_SUBSCRIPTION_ID,
+ activeSubscription.portIndex,
+ callbackIntent,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
new file mode 100644
index 000000000000..e789475b7877
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.shared.flag
+
+import com.android.systemui.Flags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import dagger.Module
+import dagger.Provides
+
+interface ComposeBouncerFlags {
+
+ /**
+ * Returns `true` if the Compose bouncer is enabled or if the scene container framework is
+ * enabled; `false` otherwise.
+ */
+ fun isComposeBouncerOrSceneContainerEnabled(): Boolean
+
+ /**
+ * Returns `true` if only compose bouncer is enabled and scene container framework is not
+ * enabled.
+ */
+ @Deprecated(
+ "Avoid using this, this is meant to be used only by the glue code " +
+ "that includes compose bouncer in legacy keyguard.",
+ replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
+ )
+ fun isOnlyComposeBouncerEnabled(): Boolean
+}
+
+class ComposeBouncerFlagsImpl(private val sceneContainerFlags: SceneContainerFlags) :
+ ComposeBouncerFlags {
+
+ override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
+ return sceneContainerFlags.isEnabled() || Flags.composeBouncer()
+ }
+
+ @Deprecated(
+ "Avoid using this, this is meant to be used only by the glue code " +
+ "that includes compose bouncer in legacy keyguard.",
+ replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
+ )
+ override fun isOnlyComposeBouncerEnabled(): Boolean {
+ return !sceneContainerFlags.isEnabled() && Flags.composeBouncer()
+ }
+}
+
+@Module
+object ComposeBouncerFlagsModule {
+ @Provides
+ @SysUISingleton
+ fun impl(sceneContainerFlags: SceneContainerFlags): ComposeBouncerFlags {
+ return ComposeBouncerFlagsImpl(sceneContainerFlags)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index dd253a8f6eff..f1a0e5e3539c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -4,14 +4,13 @@ import android.view.ViewGroup
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.ViewMediatorCallback
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.Flags
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.Flags.COMPOSE_BOUNCER_ENABLED
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -55,13 +54,12 @@ constructor(
class BouncerViewBinder
@Inject
constructor(
+ private val composeBouncerFlags: ComposeBouncerFlags,
private val legacyBouncerDependencies: Lazy<LegacyBouncerDependencies>,
private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>,
) {
fun bind(view: ViewGroup) {
- if (
- ComposeFacade.isComposeAvailable() && Flags.composeBouncer() && COMPOSE_BOUNCER_ENABLED
- ) {
+ if (COMPOSE_BOUNCER_ENABLED && composeBouncerFlags.isOnlyComposeBouncerEnabled()) {
val deps = composeBouncerDependencies.get()
ComposeBouncerViewBinder.bind(
view,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index 7b053956091e..179fa874db79 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -1,15 +1,17 @@
package com.android.systemui.bouncer.ui.binder
import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.theme.PlatformTheme
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.composable.BouncerContent
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.flow.collectLatest
@@ -27,12 +29,11 @@ object ComposeBouncerViewBinder {
viewMediatorCallback: ViewMediatorCallback?,
) {
view.addView(
- ComposeFacade.createBouncer(
- view.context,
- viewModel,
- dialogFactory,
- )
+ ComposeView(view.context).apply {
+ setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
+ }
)
+
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 4466cbbe05be..62875783ef5f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -28,6 +28,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
@@ -35,7 +36,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
@@ -72,7 +72,7 @@ class BouncerViewModel(
private val simBouncerInteractor: SimBouncerInteractor,
private val authenticationInteractor: AuthenticationInteractor,
private val selectedUserInteractor: SelectedUserInteractor,
- flags: SceneContainerFlags,
+ flags: ComposeBouncerFlags,
selectedUser: Flow<UserViewModel>,
users: Flow<List<UserViewModel>>,
userSwitcherMenu: Flow<List<UserActionViewModel>>,
@@ -233,7 +233,7 @@ class BouncerViewModel(
private var lockoutCountdownJob: Job? = null
init {
- if (flags.isEnabled()) {
+ if (flags.isComposeBouncerOrSceneContainerEnabled()) {
// Keeps the lockout dialog up-to-date.
applicationScope.launch {
bouncerInteractor.onLockoutStarted.collect {
@@ -478,7 +478,7 @@ object BouncerViewModelModule {
actionButtonInteractor: BouncerActionButtonInteractor,
authenticationInteractor: AuthenticationInteractor,
selectedUserInteractor: SelectedUserInteractor,
- flags: SceneContainerFlags,
+ flags: ComposeBouncerFlags,
userSwitcherViewModel: UserSwitcherViewModel,
clock: SystemClock,
devicePolicyManager: DevicePolicyManager,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index 1c8b84d82a56..b42eda108d54 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -73,6 +73,14 @@ class PasswordBouncerViewModel(
initialValue = isInputEnabled.value && !isTextFieldFocused.value,
)
+ /** The ID of the currently-selected user. */
+ val selectedUserId: StateFlow<Int> =
+ selectedUserInteractor.selectedUser.stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = selectedUserInteractor.getSelectedUserId(),
+ )
+
override fun onHidden() {
super.onHidden()
isTextFieldFocused.value = false
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraRotationModule.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraRotationModule.kt
new file mode 100644
index 000000000000..f12382801887
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraRotationModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera
+
+import com.android.systemui.camera.data.repository.CameraAutoRotateRepository
+import com.android.systemui.camera.data.repository.CameraAutoRotateRepositoryImpl
+import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepository
+import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+/** Module for repositories that provide data regarding camera rotation state. */
+@Module
+interface CameraRotationModule {
+
+ @Binds
+ fun bindsPrivacyRepoImpl(impl: CameraSensorPrivacyRepositoryImpl): CameraSensorPrivacyRepository
+ @Binds fun bindsRotateRepoImpl(impl: CameraAutoRotateRepositoryImpl): CameraAutoRotateRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepository.kt b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepository.kt
new file mode 100644
index 000000000000..023fd285c6d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepository.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.os.UserHandle
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+interface CameraAutoRotateRepository {
+ /** @return true if camera auto rotate setting is enabled */
+ fun isCameraAutoRotateSettingEnabled(userHandle: UserHandle): StateFlow<Boolean>
+}
+
+@SysUISingleton
+class CameraAutoRotateRepositoryImpl
+@Inject
+constructor(
+ private val secureSettings: SecureSettings,
+ @Background private val bgCoroutineContext: CoroutineContext,
+ @Application private val applicationScope: CoroutineScope,
+) : CameraAutoRotateRepository {
+ private val userMap = mutableMapOf<Int, StateFlow<Boolean>>()
+
+ override fun isCameraAutoRotateSettingEnabled(userHandle: UserHandle): StateFlow<Boolean> {
+ return userMap.getOrPut(userHandle.identifier) {
+ secureSettings
+ .observerFlow(userHandle.identifier, Settings.Secure.CAMERA_AUTOROTATE)
+ .map { isAutoRotateSettingEnabled(userHandle.identifier) }
+ .onStart { emit(isAutoRotateSettingEnabled(userHandle.identifier)) }
+ .flowOn(bgCoroutineContext)
+ .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false)
+ }
+ }
+
+ private fun isAutoRotateSettingEnabled(userId: Int) =
+ secureSettings.getIntForUser(SETTING_NAME, DISABLED, userId) == ENABLED
+
+ private companion object {
+ const val SETTING_NAME = Settings.Secure.CAMERA_AUTOROTATE
+ const val DISABLED = 0
+ const val ENABLED = 1
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt
new file mode 100644
index 000000000000..7816a1487c01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.Sensors.CAMERA
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+interface CameraSensorPrivacyRepository {
+ /** Tracks whether camera sensor privacy is enabled. */
+ fun isEnabled(userHandle: UserHandle): StateFlow<Boolean>
+}
+
+@SysUISingleton
+class CameraSensorPrivacyRepositoryImpl
+@Inject
+constructor(
+ @Background private val bgCoroutineContext: CoroutineContext,
+ @Application private val scope: CoroutineScope,
+ private val privacyManager: SensorPrivacyManager,
+) : CameraSensorPrivacyRepository {
+ private val userMap = mutableMapOf<Int, StateFlow<Boolean>>()
+
+ /** Whether camera sensor privacy is enabled */
+ override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> =
+ userMap.getOrPut(userHandle.identifier) {
+ privacyManager
+ .isEnabled(userHandle)
+ .flowOn(bgCoroutineContext)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ }
+}
+
+fun SensorPrivacyManager.isEnabled(userHandle: UserHandle): Flow<Boolean> {
+ return conflatedCallbackFlow {
+ val privacyCallback =
+ SensorPrivacyManager.OnSensorPrivacyChangedListener { sensor, enabled ->
+ if (sensor == CAMERA) {
+ trySend(enabled)
+ }
+ }
+ addSensorPrivacyListener(CAMERA, userHandle.identifier, privacyCallback)
+ awaitClose { removeSensorPrivacyListener(privacyCallback) }
+ }
+ .onStart { emit(isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, CAMERA)) }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 8397372e0735..c3c7411c401d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -16,25 +16,23 @@
package com.android.systemui.communal
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dock.DockManager
-import com.android.systemui.dock.retrieveIsDocked
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
@@ -65,38 +63,40 @@ constructor(
.onEach { nextScene -> communalInteractor.onSceneChanged(nextScene) }
.launchIn(applicationScope)
+ // TODO(b/322787129): re-enable once custom animations are in place
// Handle automatically switching to communal when docked.
- dockManager
- .retrieveIsDocked()
- // Allow some time after docking to ensure the dream doesn't start. If the dream
- // starts, then we don't want to automatically transition to glanceable hub.
- .debounce(DOCK_DEBOUNCE_DELAY)
- .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair)
- .onEach { (docked, lastStartedState) ->
- if (docked && lastStartedState == KeyguardState.LOCKSCREEN) {
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- }
- }
- .launchIn(bgScope)
+ // dockManager
+ // .retrieveIsDocked()
+ // // Allow some time after docking to ensure the dream doesn't start. If the
+ // dream
+ // // starts, then we don't want to automatically transition to glanceable hub.
+ // .debounce(DOCK_DEBOUNCE_DELAY)
+ // .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair)
+ // .onEach { (docked, lastStartedState) ->
+ // if (docked && lastStartedState == KeyguardState.LOCKSCREEN) {
+ // communalInteractor.onSceneChanged(CommunalScenes.Communal)
+ // }
+ // }
+ // .launchIn(bgScope)
}
private suspend fun determineSceneAfterTransition(
lastStartedTransition: TransitionStep,
- ): CommunalSceneKey? {
+ ): SceneKey? {
val to = lastStartedTransition.to
val from = lastStartedTransition.from
val docked = dockManager.isDocked
return when {
- docked && to == KeyguardState.LOCKSCREEN && from != KeyguardState.GLANCEABLE_HUB -> {
- CommunalSceneKey.Communal
+ docked && to == KeyguardState.LOCKSCREEN && from == KeyguardState.DREAMING -> {
+ CommunalScenes.Communal
}
- to == KeyguardState.GONE -> CommunalSceneKey.Blank
+ to == KeyguardState.GONE -> CommunalScenes.Blank
!docked && !KeyguardState.deviceIsAwakeInState(to) -> {
// If the user taps the screen and wakes the device within this timeout, we don't
// want to dismiss the hub
delay(AWAKE_DEBOUNCE_DELAY)
- CommunalSceneKey.Blank
+ CommunalScenes.Blank
}
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index f4a3bcb7a0fa..201ce832cc41 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -16,8 +16,9 @@
package com.android.systemui.communal.data.repository
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.data.repository.SceneContainerRepository
@@ -40,20 +41,20 @@ interface CommunalRepository {
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [setDesiredScene].
*/
- val desiredScene: StateFlow<CommunalSceneKey>
+ val desiredScene: StateFlow<SceneKey>
/** Exposes the transition state of the communal [SceneTransitionLayout]. */
- val transitionState: StateFlow<ObservableCommunalTransitionState>
+ val transitionState: StateFlow<ObservableTransitionState>
/** Updates the requested scene. */
- fun setDesiredScene(desiredScene: CommunalSceneKey)
+ fun setDesiredScene(desiredScene: SceneKey)
/**
* Updates the transition state of the hub [SceneTransitionLayout].
*
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
- fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?)
+ fun setTransitionState(transitionState: Flow<ObservableTransitionState>?)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -66,14 +67,12 @@ constructor(
sceneContainerRepository: SceneContainerRepository,
) : CommunalRepository {
- private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
- MutableStateFlow(CommunalSceneKey.DEFAULT)
- override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow()
+ private val _desiredScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default)
+ override val desiredScene: StateFlow<SceneKey> = _desiredScene.asStateFlow()
- private val defaultTransitionState =
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)
- private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null)
- override val transitionState: StateFlow<ObservableCommunalTransitionState> =
+ private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default)
+ private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
+ override val transitionState: StateFlow<ObservableTransitionState> =
_transitionState
.flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) }
.stateIn(
@@ -82,7 +81,7 @@ constructor(
initialValue = defaultTransitionState,
)
- override fun setDesiredScene(desiredScene: CommunalSceneKey) {
+ override fun setDesiredScene(desiredScene: SceneKey) {
_desiredScene.value = desiredScene
}
@@ -91,7 +90,7 @@ constructor(
*
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
- override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ override fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
_transitionState.value = transitionState
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 151e1eeaefc5..814295787b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -19,6 +19,8 @@ package com.android.systemui.communal.domain.interactor
import android.app.smartspace.SmartspaceTarget
import android.content.ComponentName
import android.os.UserHandle
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalPrefsRepository
import com.android.systemui.communal.data.repository.CommunalRepository
@@ -29,9 +31,8 @@ import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalContentSize.FULL
import com.android.systemui.communal.shared.model.CommunalContentSize.HALF
import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -46,7 +47,7 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
@@ -131,34 +132,33 @@ constructor(
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [onSceneChanged].
*
- * If [isCommunalAvailable] is false, will return [CommunalSceneKey.Blank]
+ * If [isCommunalAvailable] is false, will return [CommunalScenes.Blank]
*/
- val desiredScene: Flow<CommunalSceneKey> =
+ val desiredScene: Flow<SceneKey> =
communalRepository.desiredScene.combine(isCommunalAvailable) { scene, available ->
- if (available) scene else CommunalSceneKey.Blank
+ if (available) scene else CommunalScenes.Blank
}
/** Transition state of the hub mode. */
- val transitionState: StateFlow<ObservableCommunalTransitionState> =
- communalRepository.transitionState
+ val transitionState: StateFlow<ObservableTransitionState> = communalRepository.transitionState
/**
* Updates the transition state of the hub [SceneTransitionLayout].
*
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
- fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
communalRepository.setTransitionState(transitionState)
}
/** Returns a flow that tracks the progress of transitions to the given scene from 0-1. */
- fun transitionProgressToScene(targetScene: CommunalSceneKey) =
+ fun transitionProgressToScene(targetScene: SceneKey) =
transitionState
.flatMapLatest { state ->
when (state) {
- is ObservableCommunalTransitionState.Idle ->
+ is ObservableTransitionState.Idle ->
flowOf(CommunalTransitionProgress.Idle(state.scene))
- is ObservableCommunalTransitionState.Transition ->
+ is ObservableTransitionState.Transition ->
if (state.toScene == targetScene) {
state.progress.map {
CommunalTransitionProgress.Transition(
@@ -176,7 +176,7 @@ constructor(
/**
* Flow that emits a boolean if the communal UI is the target scene, ie. the [desiredScene] is
- * the [CommunalSceneKey.Communal].
+ * the [CommunalScenes.Communal].
*
* This will be true as soon as the desired scene is set programmatically or at whatever point
* during a fling that SceneTransitionLayout determines that the end state will be the communal
@@ -191,9 +191,9 @@ constructor(
flow { emit(sceneContainerFlags.isEnabled()) }
.flatMapLatest { sceneContainerEnabled ->
if (sceneContainerEnabled) {
- sceneInteractor.currentScene.map { it == SceneKey.Communal }
+ sceneInteractor.currentScene.map { it == Scenes.Communal }
} else {
- desiredScene.map { it == CommunalSceneKey.Communal }
+ desiredScene.map { it == CommunalScenes.Communal }
}
}
.distinctUntilChanged()
@@ -220,7 +220,7 @@ constructor(
*/
val isIdleOnCommunal: Flow<Boolean> =
communalRepository.transitionState.map {
- it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Communal
+ it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Communal
}
/**
@@ -230,11 +230,11 @@ constructor(
*/
val isCommunalVisible: Flow<Boolean> =
communalRepository.transitionState.map {
- !(it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Blank)
+ !(it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Blank)
}
/** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
- fun onSceneChanged(newScene: CommunalSceneKey) {
+ fun onSceneChanged(newScene: SceneKey) {
communalRepository.setDesiredScene(newScene)
}
@@ -422,7 +422,7 @@ constructor(
/** Simplified transition progress data class for tracking a single transition between scenes. */
sealed class CommunalTransitionProgress {
/** No transition/animation is currently running. */
- data class Idle(val scene: CommunalSceneKey) : CommunalTransitionProgress()
+ data class Idle(val scene: SceneKey) : CommunalTransitionProgress()
/** There is a transition animating to the expected scene. */
data class Transition(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
index 889023e8dab6..f2b473864a78 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
@@ -16,12 +16,12 @@
package com.android.systemui.communal.log
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.UiEventLogger
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.kotlin.pairwise
@@ -87,25 +87,25 @@ constructor(
}
/** Whether currently in communal scene. */
-private fun ObservableCommunalTransitionState.isOnCommunal(): Boolean {
- return this is ObservableCommunalTransitionState.Idle && scene == CommunalSceneKey.Communal
+private fun ObservableTransitionState.isOnCommunal(): Boolean {
+ return this is ObservableTransitionState.Idle && scene == CommunalScenes.Communal
}
/** Whether currently in a scene other than communal. */
-private fun ObservableCommunalTransitionState.isNotOnCommunal(): Boolean {
- return this is ObservableCommunalTransitionState.Idle && scene != CommunalSceneKey.Communal
+private fun ObservableTransitionState.isNotOnCommunal(): Boolean {
+ return this is ObservableTransitionState.Idle && scene != CommunalScenes.Communal
}
/** Whether currently transitioning from another scene to communal. */
-private fun ObservableCommunalTransitionState.isSwipingToCommunal(): Boolean {
- return this is ObservableCommunalTransitionState.Transition &&
- toScene == CommunalSceneKey.Communal &&
+private fun ObservableTransitionState.isSwipingToCommunal(): Boolean {
+ return this is ObservableTransitionState.Transition &&
+ toScene == CommunalScenes.Communal &&
isInitiatedByUserInput
}
/** Whether currently transitioning from communal to another scene. */
-private fun ObservableCommunalTransitionState.isSwipingFromCommunal(): Boolean {
- return this is ObservableCommunalTransitionState.Transition &&
- fromScene == CommunalSceneKey.Communal &&
+private fun ObservableTransitionState.isSwipingFromCommunal(): Boolean {
+ return this is ObservableTransitionState.Transition &&
+ fromScene == CommunalScenes.Communal &&
isInitiatedByUserInput
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalScenes.kt
index c68dd4ff271c..d5a56c1e9ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalScenes.kt
@@ -16,21 +16,15 @@
package com.android.systemui.communal.shared.model
-/** Definition of the possible scenes for the communal UI. */
-sealed class CommunalSceneKey(
- private val loggingName: String,
-) {
- /** The communal scene containing the hub UI. */
- object Communal : CommunalSceneKey("communal")
+import com.android.compose.animation.scene.SceneKey
+/** Definition of the possible scenes for the communal UI. */
+object CommunalScenes {
/** The default scene, shows nothing and is only there to allow swiping to communal. */
- object Blank : CommunalSceneKey("blank")
+ @JvmField val Blank = SceneKey("blank")
- override fun toString(): String {
- return loggingName
- }
+ /** The communal scene containing the hub UI. */
+ @JvmField val Communal = SceneKey("communal")
- companion object {
- val DEFAULT = Blank
- }
+ @JvmField val Default = Blank
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt
deleted file mode 100644
index d834715768c9..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.shared.model
-
-import kotlinx.coroutines.flow.Flow
-
-/**
- * This is a fork of the `com.android.compose.animation.scene.ObservableTransitionState` class.
- *
- * TODO(b/315490861): remove this fork, once we can compile Compose into System UI.
- */
-sealed class ObservableCommunalTransitionState {
- /** No transition/animation is currently running. */
- data class Idle(val scene: CommunalSceneKey) : ObservableCommunalTransitionState()
-
- /** There is a transition animating between two scenes. */
- data class Transition(
- val fromScene: CommunalSceneKey,
- val toScene: CommunalSceneKey,
- val progress: Flow<Float>,
-
- /**
- * Whether the transition was originally triggered by user input rather than being
- * programmatic. If this value is initially true, it will remain true until the transition
- * fully completes, even if the user input that triggered the transition has ended. Any
- * sub-transitions launched by this one will inherit this value. For example, if the user
- * drags a pointer but does not exceed the threshold required to transition to another
- * scene, this value will remain true after the pointer is no longer touching the screen and
- * will be true in any transition created to animate back to the original position.
- */
- val isInitiatedByUserInput: Boolean,
-
- /**
- * Whether user input is currently driving the transition. For example, if a user is
- * dragging a pointer, this emits true. Once they lift their finger, this emits false while
- * the transition completes/settles.
- */
- val isUserInputOngoing: Flow<Boolean>,
- ) : ObservableCommunalTransitionState()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 3ec9a268f80c..35372cd28c15 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -18,10 +18,10 @@ package com.android.systemui.communal.ui.viewmodel
import android.content.ComponentName
import android.os.UserHandle
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.media.controls.ui.view.MediaHost
import kotlinx.coroutines.flow.Flow
@@ -34,7 +34,7 @@ abstract class BaseCommunalViewModel(
private val communalInteractor: CommunalInteractor,
val mediaHost: MediaHost,
) {
- val currentScene: Flow<CommunalSceneKey> = communalInteractor.desiredScene
+ val currentScene: Flow<SceneKey> = communalInteractor.desiredScene
/** Whether widgets are currently being re-ordered. */
open val reorderingWidgets: StateFlow<Boolean> = MutableStateFlow(false)
@@ -45,7 +45,7 @@ abstract class BaseCommunalViewModel(
val selectedKey: StateFlow<String?>
get() = _selectedKey
- fun onSceneChanged(scene: CommunalSceneKey) {
+ fun onSceneChanged(scene: SceneKey) {
communalInteractor.onSceneChanged(scene)
}
@@ -54,7 +54,7 @@ abstract class BaseCommunalViewModel(
*
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
- fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
communalInteractor.setTransitionState(transitionState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index a5a390d7683b..b6ad26b24dc7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -25,14 +25,21 @@ import android.util.Log
import android.view.IWindowManager
import android.view.WindowInsets
import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.compose.theme.PlatformTheme
import com.android.internal.logging.UiEventLogger
import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
-import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
@@ -110,56 +117,68 @@ constructor(
val preselectedKey = intent.getStringExtra(EXTRA_PRESELECTED_KEY)
communalViewModel.setSelectedKey(preselectedKey)
- setCommunalEditWidgetActivityContent(
- activity = this,
- viewModel = communalViewModel,
- widgetConfigurator = widgetConfigurator,
- onOpenWidgetPicker = {
- val intent =
- Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) }
- packageManager
- .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
- ?.activityInfo
- ?.packageName
- ?.let { packageName ->
- try {
- addWidgetActivityLauncher.launch(
- Intent(Intent.ACTION_PICK).apply {
- setPackage(packageName)
- putExtra(
- EXTRA_DESIRED_WIDGET_WIDTH,
- resources.getDimensionPixelSize(
- R.dimen.communal_widget_picker_desired_width
- )
- )
- putExtra(
- EXTRA_DESIRED_WIDGET_HEIGHT,
- resources.getDimensionPixelSize(
- R.dimen.communal_widget_picker_desired_height
- )
- )
- putExtra(
- AppWidgetManager.EXTRA_CATEGORY_FILTER,
- communalViewModel.getCommunalWidgetCategories
- )
- }
+ setContent {
+ PlatformTheme {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .background(LocalAndroidColorScheme.current.outlineVariant),
+ ) {
+ CommunalHub(
+ viewModel = communalViewModel,
+ onOpenWidgetPicker = ::onOpenWidgetPicker,
+ widgetConfigurator = widgetConfigurator,
+ onEditDone = ::onEditDone,
+ )
+ }
+ }
+ }
+ }
+
+ private fun onOpenWidgetPicker() {
+ val intent = Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) }
+ packageManager
+ .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ ?.activityInfo
+ ?.packageName
+ ?.let { packageName ->
+ try {
+ addWidgetActivityLauncher.launch(
+ Intent(Intent.ACTION_PICK).apply {
+ setPackage(packageName)
+ putExtra(
+ EXTRA_DESIRED_WIDGET_WIDTH,
+ resources.getDimensionPixelSize(
+ R.dimen.communal_widget_picker_desired_width
+ )
+ )
+ putExtra(
+ EXTRA_DESIRED_WIDGET_HEIGHT,
+ resources.getDimensionPixelSize(
+ R.dimen.communal_widget_picker_desired_height
+ )
+ )
+ putExtra(
+ AppWidgetManager.EXTRA_CATEGORY_FILTER,
+ communalViewModel.getCommunalWidgetCategories
)
- } catch (e: Exception) {
- Log.e(TAG, "Failed to launch widget picker activity", e)
}
- }
- ?: run { Log.e(TAG, "Couldn't resolve launcher package name") }
- },
- onEditDone = {
- try {
- communalViewModel.onSceneChanged(CommunalSceneKey.Communal)
- checkNotNull(windowManagerService).lockNow(/* options */ null)
- finish()
- } catch (e: RemoteException) {
- Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to launch widget picker activity", e)
}
}
- )
+ ?: run { Log.e(TAG, "Couldn't resolve launcher package name") }
+ }
+
+ private fun onEditDone() {
+ try {
+ communalViewModel.onSceneChanged(CommunalScenes.Communal)
+ checkNotNull(windowManagerService).lockNow(/* options */ null)
+ finish()
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
+ }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
deleted file mode 100644
index a0aaa906802a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.compose
-
-import android.content.Context
-import android.view.View
-import android.view.WindowInsets
-import androidx.activity.ComponentActivity
-import androidx.lifecycle.LifecycleOwner
-import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.scene.shared.model.Scene
-import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.StateFlow
-
-/**
- * A facade to interact with Compose, when it is available.
- *
- * You should access this facade by calling the static methods on
- * [com.android.systemui.compose.ComposeFacade] directly.
- */
-interface BaseComposeFacade {
- /**
- * Whether Compose is currently available. This function should be checked before calling any
- * other functions on this facade.
- *
- * This value will never change at runtime.
- */
- fun isComposeAvailable(): Boolean
-
- /**
- * Return the [ComposeInitializer] to make Compose usable in windows outside normal activities.
- */
- fun composeInitializer(): ComposeInitializer
-
- /** Bind the content of [activity] to [viewModel]. */
- fun setPeopleSpaceActivityContent(
- activity: ComponentActivity,
- viewModel: PeopleViewModel,
- onResult: (PeopleViewModel.Result) -> Unit,
- )
-
- /** Bind the content of [activity] to [viewModel]. */
- fun setCommunalEditWidgetActivityContent(
- activity: ComponentActivity,
- viewModel: BaseCommunalViewModel,
- widgetConfigurator: WidgetConfigurator,
- onOpenWidgetPicker: () -> Unit,
- onEditDone: () -> Unit,
- )
-
- fun setVolumePanelActivityContent(
- activity: ComponentActivity,
- viewModel: VolumePanelViewModel,
- onDismiss: () -> Unit,
- )
-
- /** Create a [View] to represent [viewModel] on screen. */
- fun createFooterActionsView(
- context: Context,
- viewModel: FooterActionsViewModel,
- qsVisibilityLifecycleOwner: LifecycleOwner,
- ): View
-
- /** Create a [View] to represent [viewModel] on screen. */
- fun createSceneContainerView(
- scope: CoroutineScope,
- context: Context,
- viewModel: SceneContainerViewModel,
- windowInsets: StateFlow<WindowInsets?>,
- sceneByKey: Map<SceneKey, Scene>,
- dataSourceDelegator: SceneDataSourceDelegator,
- ): View
-
- /** Creates sticky key indicator content presenting provided [viewModel] */
- fun createStickyKeysIndicatorContent(
- context: Context,
- viewModel: StickyKeysIndicatorViewModel
- ): View
-
- /** Create a [View] to represent [viewModel] on screen. */
- fun createCommunalView(
- context: Context,
- viewModel: BaseCommunalViewModel,
- ): View
-
- /** Create a [View] to represent the [BouncerViewModel]. */
- fun createBouncer(
- context: Context,
- viewModel: BouncerViewModel,
- dialogFactory: BouncerDialogFactory,
- ): View
-
- /** Creates a container that hosts the communal UI and handles gesture transitions. */
- fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View
-
- /** Creates a [View] that represents the Lockscreen. */
- fun createLockscreen(
- context: Context,
- viewModel: LockscreenContentViewModel,
- blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
- ): View
-}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
index 90dc3a00daa2..813e0e025bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
@@ -17,6 +17,13 @@
package com.android.systemui.compose
import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.findViewTreeLifecycleOwner
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import com.android.compose.animation.ViewTreeSavedStateRegistryOwner
+import com.android.systemui.lifecycle.ViewLifecycleOwner
/**
* An initializer to use Compose outside of an Activity, e.g. inside a window added directly using
@@ -39,10 +46,55 @@ import android.view.View
* }
* ```
*/
-interface ComposeInitializer {
+object ComposeInitializer {
/** Function to be called on your window root view's [View.onAttachedToWindow] function. */
- fun onAttachedToWindow(root: View)
+ fun onAttachedToWindow(root: View) {
+ if (root.findViewTreeLifecycleOwner() != null) {
+ error("root $root already has a LifecycleOwner")
+ }
+
+ val parent = root.parent
+ if (parent is View && parent.id != android.R.id.content) {
+ error(
+ "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." +
+ "Outside of activities and dialogs, this is usually the top-most View of a " +
+ "window."
+ )
+ }
+
+ // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is
+ // both visible and focused.
+ val lifecycleOwner = ViewLifecycleOwner(root)
+
+ // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save
+ // or restore because SystemUI process is always running and top-level windows using this
+ // initializer are created once, when the process is started.
+ val savedStateRegistryOwner =
+ object : SavedStateRegistryOwner {
+ private val savedStateRegistryController =
+ SavedStateRegistryController.create(this).apply { performRestore(null) }
+
+ override val savedStateRegistry = savedStateRegistryController.savedStateRegistry
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleOwner.lifecycle
+ }
+
+ // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner]
+ // because `onCreate` might move the lifecycle state to STARTED which will make
+ // [SavedStateRegistryController.performRestore] throw.
+ lifecycleOwner.onCreate()
+
+ // Set the owners on the root. They will be reused by any ComposeView inside the root
+ // hierarchy.
+ root.setViewTreeLifecycleOwner(lifecycleOwner)
+ ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner)
+ }
/** Function to be called on your window root view's [View.onDetachedFromWindow] function. */
- fun onDetachedFromWindow(root: View)
+ fun onDetachedFromWindow(root: View) {
+ (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy()
+ root.setViewTreeLifecycleOwner(null)
+ ViewTreeSavedStateRegistryOwner.set(root, null)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index f7bc5cdc69c2..a4011fd7718c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -43,6 +43,7 @@ import com.android.systemui.reardisplay.RearDisplayModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
import com.android.systemui.rotationlock.RotationLockModule;
+import com.android.systemui.rotationlock.RotationLockNewModule;
import com.android.systemui.scene.SceneContainerFrameworkModule;
import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.settings.MultiUserUtilsModule;
@@ -110,6 +111,7 @@ import javax.inject.Named;
RearDisplayModule.class,
ReferenceScreenshotModule.class,
RotationLockModule.class,
+ RotationLockNewModule.class,
ScreenDecorationsModule.class,
SystemActionsModule.class,
ShadeModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1157d97f2f2e..19af371d1dfa 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -51,6 +51,7 @@ import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.deviceentry.DeviceEntryModule;
import com.android.systemui.display.DisplayModule;
@@ -365,7 +366,8 @@ public abstract class SystemUIModule {
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- @Main Executor sysuiMainExecutor) {
+ @Main Executor sysuiMainExecutor,
+ @UiBackground Executor sysuiUiBgExecutor) {
return Optional.ofNullable(BubblesManager.create(context,
bubblesOptional,
notificationShadeWindowController,
@@ -384,7 +386,8 @@ public abstract class SystemUIModule {
sysUiState,
featureFlags,
notifPipelineFlags,
- sysuiMainExecutor));
+ sysuiMainExecutor,
+ sysuiUiBgExecutor));
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index cf7d60140aee..baae986c494d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -62,6 +62,10 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import com.google.errorprone.annotations.CompileTimeConstant
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -83,10 +87,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import java.io.PrintWriter
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
/**
* API to run face authentication and detection for device entry / on keyguard (as opposed to the
@@ -431,11 +431,11 @@ constructor(
override fun onAuthenticationFailed() {
_isAuthenticated.value = false
faceAuthLogger.authenticationFailed()
+ _authenticationStatus.value = FailedFaceAuthenticationStatus()
if (!_isLockedOut.value) {
// onAuthenticationError gets invoked before onAuthenticationFailed when the
// last auth attempt locks out face authentication.
- // Skip updating the authentication status in such a scenario.
- _authenticationStatus.value = FailedFaceAuthenticationStatus()
+ // Skip onFaceAuthRequestCompleted in such a scenario.
onFaceAuthRequestCompleted()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
index 6bfe8d91b5fc..846013cef326 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
@@ -21,9 +21,12 @@ import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInte
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceFailureMessage
+import com.android.systemui.deviceentry.shared.model.FaceLockoutMessage
import com.android.systemui.deviceentry.shared.model.FaceMessage
import com.android.systemui.deviceentry.shared.model.FaceTimeoutMessage
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FingerprintFailureMessage
import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage
import com.android.systemui.deviceentry.shared.model.FingerprintMessage
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
@@ -97,11 +100,7 @@ constructor(
fingerprintAuthInteractor.fingerprintHelp
.sample(biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed, ::Pair)
.filter { (_, fingerprintAuthAllowed) -> fingerprintAuthAllowed }
- .map { (helpStatus, _) ->
- FingerprintMessage(
- helpStatus.msg,
- )
- }
+ .map { (helpStatus, _) -> FingerprintMessage(helpStatus.msg) }
private val fingerprintFailMessage: Flow<FingerprintMessage> =
fingerprintPropertyInteractor.isUdfps.flatMapLatest { isUdfps ->
@@ -109,7 +108,7 @@ constructor(
.sample(biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed)
.filter { fingerprintAuthAllowed -> fingerprintAuthAllowed }
.map {
- FingerprintMessage(
+ FingerprintFailureMessage(
if (isUdfps) {
resources.getString(
com.android.internal.R.string.fingerprint_udfps_error_not_match
@@ -118,7 +117,7 @@ constructor(
resources.getString(
com.android.internal.R.string.fingerprint_error_not_match
)
- },
+ }
)
}
}
@@ -154,7 +153,7 @@ constructor(
faceFailure
.sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed)
.filter { faceAuthCurrentlyAllowed -> faceAuthCurrentlyAllowed }
- .map { FaceMessage(resources.getString(R.string.keyguard_face_failed)) }
+ .map { FaceFailureMessage(resources.getString(R.string.keyguard_face_failed)) }
private val faceErrorMessage: Flow<FaceMessage> =
faceError
@@ -173,6 +172,7 @@ constructor(
FaceTimeoutMessage(status.msg)
}
}
+ status.isLockoutError() -> FaceLockoutMessage(status.msg)
else -> FaceMessage(status.msg)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 21fd87c1c241..029a4f33cd27 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -25,7 +25,7 @@ import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -81,9 +81,9 @@ constructor(
val isDeviceEntered: StateFlow<Boolean> =
sceneInteractor.currentScene
.filter { currentScene ->
- currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen
+ currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen
}
- .map { it == SceneKey.Gone }
+ .map { it == Scenes.Gone }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -148,12 +148,12 @@ constructor(
applicationScope.launch {
if (isAuthenticationRequired()) {
sceneInteractor.changeScene(
- toScene = SceneKey.Bouncer,
+ toScene = Scenes.Bouncer,
loggingReason = "request to unlock device while authentication required",
)
} else {
sceneInteractor.changeScene(
- toScene = SceneKey.Gone,
+ toScene = Scenes.Gone,
loggingReason = "request to unlock device while authentication isn't required",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
index 79455ebb624c..289dbd9f66e0 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
@@ -24,9 +24,12 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.model.BiometricMessage
import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -60,17 +63,23 @@ constructor(
private val context: Context,
activityStarter: ActivityStarter,
powerInteractor: PowerInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
private val keyguardOccludedByApp: Flow<Boolean> =
- combine(
- keyguardInteractor.isKeyguardOccluded,
- keyguardInteractor.isKeyguardShowing,
- primaryBouncerInteractor.isShowing,
- alternateBouncerInteractor.isVisible,
- ) { occluded, showing, primaryBouncerShowing, alternateBouncerVisible ->
- occluded && showing && !primaryBouncerShowing && !alternateBouncerVisible
- }
- .distinctUntilChanged()
+ if (KeyguardWmStateRefactor.isEnabled) {
+ keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.OCCLUDED }
+ } else {
+ combine(
+ keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.isKeyguardShowing,
+ primaryBouncerInteractor.isShowing,
+ alternateBouncerInteractor.isVisible,
+ ) { occluded, showing, primaryBouncerShowing, alternateBouncerVisible ->
+ occluded && showing && !primaryBouncerShowing && !alternateBouncerVisible
+ }
+ .distinctUntilChanged()
+ }
+
private val fingerprintUnlockSuccessEvents: Flow<Unit> =
fingerprintAuthRepository.authenticationStatus
.ifKeyguardOccludedByApp()
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
index 59c3f7f8aded..2ced8c41713f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
@@ -32,9 +32,15 @@ data class FaceTimeoutMessage(
private val faceTimeoutMessage: String?,
) : FaceMessage(faceTimeoutMessage)
+data class FaceLockoutMessage(private val msg: String?) : FaceMessage(msg)
+
+data class FaceFailureMessage(private val msg: String) : FaceMessage(msg)
+
/** Fingerprint biometric message */
open class FingerprintMessage(fingerprintMessage: String?) : BiometricMessage(fingerprintMessage)
data class FingerprintLockoutMessage(
private val fingerprintLockoutMessage: String?,
) : FingerprintMessage(fingerprintLockoutMessage)
+
+data class FingerprintFailureMessage(private val msg: String?) : FingerprintMessage(msg)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt
index 931a86938592..ed82278a7346 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderHapticPlugin.kt
@@ -163,6 +163,6 @@ constructor(
}
companion object {
- const val KEY_UP_TIMEOUT = 100L
+ const val KEY_UP_TIMEOUT = 60L
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
index 3ed58a7fe5ae..89433d3e8abb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
@@ -20,15 +20,16 @@ import android.app.Dialog
import android.content.Context
import android.view.Gravity
import android.view.Window
+import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND
import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
import androidx.activity.ComponentDialog
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView
import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
import com.android.systemui.res.R
import javax.inject.Inject
@@ -45,10 +46,10 @@ constructor(
}
private fun createStickyKeyIndicator(viewModel: StickyKeysIndicatorViewModel): Dialog {
- return ComponentDialog(context, R.style.Theme_SystemUI_Dialog).apply {
+ return ComponentDialog(context, R.style.Theme_SystemUI_Dialog_StickyKeys).apply {
// because we're requesting window feature it must be called before setting content
window?.setStickyKeyWindowAttributes()
- setContentView(ComposeFacade.createStickyKeysIndicatorContent(context, viewModel))
+ setContentView(createStickyKeyIndicatorView(context, viewModel))
}
}
@@ -61,6 +62,9 @@ constructor(
attributes =
WindowManager.LayoutParams().apply {
copyFrom(attributes)
+ // needed because we're above system bars windows, see [TYPE_STATUS_BAR_SUB_PANEL]
+ receiveInsetsIgnoringZOrder = true
+ fitInsetsTypes = WindowInsets.Type.systemBars()
title = "StickyKeysIndicator"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
index 842fd04bfcc5..78c4e77cad74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
@@ -17,15 +17,13 @@
package com.android.systemui.keyboard.stickykeys.ui
import android.app.Dialog
-import android.util.Log
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
-import javax.inject.Inject
@SysUISingleton
class StickyKeysIndicatorCoordinator
@@ -40,11 +38,6 @@ constructor(
private var dialog: Dialog? = null
fun startListening() {
- // this check needs to be moved to PhysicalKeyboardCoreStartable
- if (!ComposeFacade.isComposeAvailable()) {
- Log.e("StickyKeysIndicatorCoordinator", "Compose is required for this UI")
- return
- }
applicationScope.launch {
viewModel.indicatorContent.collect { stickyKeys ->
stickyKeysLogger.logNewUiState(stickyKeys)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 2e233d8e5dd2..3134e35a92e3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -289,6 +289,8 @@ public class KeyguardService extends Service {
};
}
+ private final WindowManagerOcclusionManager mWmOcclusionManager;
+
@Inject
public KeyguardService(
KeyguardViewMediator keyguardViewMediator,
@@ -302,7 +304,8 @@ public class KeyguardService extends Service {
KeyguardSurfaceBehindParamsApplier keyguardSurfaceBehindAnimator,
@Application CoroutineScope scope,
FeatureFlags featureFlags,
- PowerInteractor powerInteractor) {
+ PowerInteractor powerInteractor,
+ WindowManagerOcclusionManager windowManagerOcclusionManager) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -323,6 +326,8 @@ public class KeyguardService extends Service {
keyguardSurfaceBehindAnimator,
scope);
}
+
+ mWmOcclusionManager = windowManagerOcclusionManager;
}
@Override
@@ -414,7 +419,11 @@ public class KeyguardService extends Service {
Trace.beginSection("KeyguardService.mBinder#setOccluded");
checkPermission();
- mKeyguardViewMediator.setOccluded(isOccluded, animate);
+ if (!KeyguardWmStateRefactor.isEnabled()) {
+ mKeyguardViewMediator.setOccluded(isOccluded, animate);
+ } else {
+ mWmOcclusionManager.onKeyguardServiceSetOccluded(isOccluded);
+ }
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 301942f6242b..106fdf1fbcbe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,6 +20,9 @@ package com.android.systemui.keyguard
import android.content.Context
import android.view.LayoutInflater
import android.view.View
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
@@ -35,7 +38,6 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
@@ -45,6 +47,8 @@ import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
+import com.android.systemui.keyguard.ui.composable.LockscreenContent
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
@@ -132,7 +136,7 @@ constructor(
if (!SceneContainerFlag.isEnabled) {
if (ComposeLockscreen.isEnabled) {
val composeView =
- ComposeFacade.createLockscreen(
+ createLockscreen(
context = context,
viewModel = lockscreenContentViewModel,
blueprints = lockscreenSceneBlueprintsLazy.get(),
@@ -207,6 +211,21 @@ constructor(
)
}
+ private fun createLockscreen(
+ context: Context,
+ viewModel: LockscreenContentViewModel,
+ blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+ ): View {
+ val sceneBlueprints =
+ blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet()
+ return ComposeView(context).apply {
+ setContent {
+ LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints)
+ .Content(modifier = Modifier.fillMaxSize())
+ }
+ }
+ }
+
/**
* Temporary, to allow NotificationPanelViewController to use the same instance while code is
* migrated: b/288242803
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6d917bbde82b..a37397db81f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1365,7 +1365,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager;
+ private WindowManagerOcclusionManager mWmOcclusionManager;
/**
+
* Injected constructor. See {@link KeyguardModule}.
*/
public KeyguardViewMediator(
@@ -1411,7 +1413,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
SystemPropertiesHelper systemPropertiesHelper,
Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
SelectedUserInteractor selectedUserInteractor,
- KeyguardInteractor keyguardInteractor) {
+ KeyguardInteractor keyguardInteractor,
+ WindowManagerOcclusionManager wmOcclusionManager) {
mContext = context;
mUserTracker = userTracker;
mFalsingCollector = falsingCollector;
@@ -1486,6 +1489,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
mShowKeyguardWakeLock.setReferenceCounted(false);
+
+ mWmOcclusionManager = wmOcclusionManager;
}
public void userActivity() {
@@ -2103,15 +2108,27 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
public IRemoteAnimationRunner getOccludeAnimationRunner() {
- return validatingRemoteAnimationRunner(mOccludeAnimationRunner);
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ return validatingRemoteAnimationRunner(mWmOcclusionManager.getOccludeAnimationRunner());
+ } else {
+ return validatingRemoteAnimationRunner(mOccludeAnimationRunner);
+ }
}
+ /**
+ * TODO(b/326464548): Move this to WindowManagerOcclusionManager
+ */
public IRemoteAnimationRunner getOccludeByDreamAnimationRunner() {
return validatingRemoteAnimationRunner(mOccludeByDreamAnimationRunner);
}
public IRemoteAnimationRunner getUnoccludeAnimationRunner() {
- return validatingRemoteAnimationRunner(mUnoccludeAnimationRunner);
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ return validatingRemoteAnimationRunner(
+ mWmOcclusionManager.getUnoccludeAnimationRunner());
+ } else {
+ return validatingRemoteAnimationRunner(mUnoccludeAnimationRunner);
+ }
}
public boolean isHiding() {
@@ -2145,8 +2162,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
if (mOccluded != isOccluded) {
mOccluded = isOccluded;
- mKeyguardViewControllerLazy.get().setOccluded(isOccluded, animate
- && mDeviceInteractive);
+ if (!KeyguardWmStateRefactor.isEnabled()) {
+ mKeyguardViewControllerLazy.get().setOccluded(isOccluded, animate
+ && mDeviceInteractive);
+ }
adjustStatusBarLocked();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 8ebcece940c2..00f50023b263 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -140,8 +140,14 @@ constructor(
nonApps: Array<RemoteAnimationTarget>,
finishedCallback: IRemoteAnimationFinishedCallback
) {
- goingAwayRemoteAnimationFinishedCallback = finishedCallback
- keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
+ if (apps.isNotEmpty()) {
+ goingAwayRemoteAnimationFinishedCallback = finishedCallback
+ keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
+ } else {
+ // Nothing to do here if we have no apps, end the animation, which will cancel it and WM
+ // will make *something* visible.
+ finishedCallback.onAnimationFinished()
+ }
}
fun onKeyguardGoingAwayRemoteAnimationCancelled() {
@@ -174,13 +180,19 @@ constructor(
* if so, true should be the right choice.
*/
private fun setWmLockscreenState(
- lockscreenShowing: Boolean = this.isLockscreenShowing ?: true.also {
- Log.d(TAG, "Using isLockscreenShowing=true default in setWmLockscreenState, " +
- "because setAodVisible was called before the first setLockscreenShown " +
- "call during boot. This is not typical, but is theoretically possible. " +
- "If you're investigating the lockscreen showing unexpectedly, start here.")
- },
- aodVisible: Boolean = this.isAodVisible
+ lockscreenShowing: Boolean =
+ this.isLockscreenShowing
+ ?: true.also {
+ Log.d(
+ TAG,
+ "Using isLockscreenShowing=true default in setWmLockscreenState, " +
+ "because setAodVisible was called before the first " +
+ "setLockscreenShown call during boot. This is not typical, but is " +
+ "theoretically possible. If you're investigating the lockscreen " +
+ "showing unexpectedly, start here."
+ )
+ },
+ aodVisible: Boolean = this.isAodVisible
) {
Log.d(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
new file mode 100644
index 000000000000..aab90c378a19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Matrix
+import android.os.RemoteException
+import android.util.Log
+import android.view.IRemoteAnimationFinishedCallback
+import android.view.IRemoteAnimationRunner
+import android.view.RemoteAnimationTarget
+import android.view.SyncRtSurfaceTransactionApplier
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+import androidx.annotation.VisibleForTesting
+import com.android.app.animation.Interpolators
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.policy.ScreenDecorationsUtils
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.TransitionAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.res.R
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+private val UNOCCLUDE_ANIMATION_DURATION = 250
+private val UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT = 0.1f
+
+/**
+ * Keeps track of Window Manager's occlusion state and RemoteAnimations related to changes in
+ * occlusion state. Occlusion is when a [FLAG_SHOW_WHEN_LOCKED] activity is displaying over the
+ * lockscreen - we're still locked, but the user can interact with the activity.
+ *
+ * Typical "occlusion" use cases include launching the camera over the lockscreen, tapping a quick
+ * affordance to bring up Google Pay/Wallet/whatever it's called by the time you're reading this,
+ * and Maps Navigation.
+ *
+ * Window Manager considers the keyguard to be 'occluded' whenever a [FLAG_SHOW_WHEN_LOCKED]
+ * activity is on top of the task stack, even if the device is unlocked and the keyguard is not
+ * visible. System UI considers the keyguard to be [KeyguardState.OCCLUDED] only when we're on the
+ * keyguard and an activity is displaying over it.
+ *
+ * For all System UI use cases, you should use [KeyguardTransitionInteractor] to determine if we're
+ * in the [KeyguardState.OCCLUDED] state and react accordingly. If you are sure that you need to
+ * check whether Window Manager considers OCCLUDED=true even though the lockscreen is not showing,
+ * use [KeyguardShowWhenLockedActivityInteractor.isShowWhenLockedActivityOnTop] in combination with
+ * [KeyguardTransitionInteractor] state.
+ *
+ * This is a very sensitive piece of state that has caused many headaches in the past. Please be
+ * careful.
+ */
+@SysUISingleton
+class WindowManagerOcclusionManager
+@Inject
+constructor(
+ val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ val activityTransitionAnimator: ActivityTransitionAnimator,
+ val keyguardViewController: dagger.Lazy<KeyguardViewController>,
+ val powerInteractor: PowerInteractor,
+ val context: Context,
+ val interactionJankMonitor: InteractionJankMonitor,
+ @Main executor: Executor,
+ val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+ val occlusionInteractor: KeyguardOcclusionInteractor,
+) {
+ val powerButtonY =
+ context.resources.getDimensionPixelSize(
+ R.dimen.physical_power_button_center_screen_location_y
+ )
+ val windowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+
+ var occludeAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
+
+ /**
+ * Animation runner provided to WindowManager, which will be used if an occluding activity is
+ * launched and Window Manager wants us to animate it in. This is used as a signal that we are
+ * now occluded, and should update our state accordingly.
+ */
+ val occludeAnimationRunner: IRemoteAnimationRunner =
+ object : IRemoteAnimationRunner.Stub() {
+ override fun onAnimationStart(
+ transit: Int,
+ apps: Array<RemoteAnimationTarget>,
+ wallpapers: Array<RemoteAnimationTarget>,
+ nonApps: Array<RemoteAnimationTarget>,
+ finishedCallback: IRemoteAnimationFinishedCallback?
+ ) {
+ Log.d(TAG, "occludeAnimationRunner#onAnimationStart")
+ // Wrap the callback so that it's guaranteed to be nulled out once called.
+ occludeAnimationFinishedCallback =
+ object : IRemoteAnimationFinishedCallback.Stub() {
+ override fun onAnimationFinished() {
+ finishedCallback?.onAnimationFinished()
+ occludeAnimationFinishedCallback = null
+ }
+ }
+ keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop = true,
+ taskInfo = apps.firstOrNull()?.taskInfo,
+ )
+ activityTransitionAnimator
+ .createRunner(occludeAnimationController)
+ .onAnimationStart(
+ transit,
+ apps,
+ wallpapers,
+ nonApps,
+ occludeAnimationFinishedCallback,
+ )
+ }
+
+ override fun onAnimationCancelled() {
+ Log.d(TAG, "occludeAnimationRunner#onAnimationCancelled")
+ }
+ }
+
+ var unoccludeAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
+
+ /**
+ * Animation runner provided to WindowManager, which will be used if an occluding activity is
+ * finished and Window Manager wants us to animate it out. This is used as a signal that we are
+ * no longer occluded, and should update our state accordingly.
+ *
+ * TODO(b/326464548): Restore dream specific animation.
+ */
+ val unoccludeAnimationRunner: IRemoteAnimationRunner =
+ object : IRemoteAnimationRunner.Stub() {
+ var unoccludeAnimator: ValueAnimator? = null
+ val unoccludeMatrix = Matrix()
+
+ /** TODO(b/326470033): Extract this logic into ViewModels. */
+ override fun onAnimationStart(
+ transit: Int,
+ apps: Array<RemoteAnimationTarget>,
+ wallpapers: Array<RemoteAnimationTarget>,
+ nonApps: Array<RemoteAnimationTarget>,
+ finishedCallback: IRemoteAnimationFinishedCallback?
+ ) {
+ Log.d(TAG, "unoccludeAnimationRunner#onAnimationStart")
+ // Wrap the callback so that it's guaranteed to be nulled out once called.
+ unoccludeAnimationFinishedCallback =
+ object : IRemoteAnimationFinishedCallback.Stub() {
+ override fun onAnimationFinished() {
+ finishedCallback?.onAnimationFinished()
+ unoccludeAnimationFinishedCallback = null
+ }
+ }
+ keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop = false,
+ taskInfo = apps.firstOrNull()?.taskInfo,
+ )
+ interactionJankMonitor.begin(
+ createInteractionJankMonitorConf(
+ InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION,
+ "UNOCCLUDE"
+ )
+ )
+ if (apps.isEmpty()) {
+ Log.d(
+ TAG,
+ "No apps provided to unocclude runner; " +
+ "skipping animation and unoccluding."
+ )
+ unoccludeAnimationFinishedCallback?.onAnimationFinished()
+ return
+ }
+ val target = apps[0]
+ val localView: View = keyguardViewController.get().getViewRootImpl().getView()
+ val applier = SyncRtSurfaceTransactionApplier(localView)
+ // TODO(
+ executor.execute {
+ unoccludeAnimator?.cancel()
+ unoccludeAnimator =
+ ValueAnimator.ofFloat(1f, 0f).apply {
+ duration = UNOCCLUDE_ANIMATION_DURATION.toLong()
+ interpolator = Interpolators.TOUCH_RESPONSE
+ addUpdateListener { animation: ValueAnimator ->
+ val animatedValue = animation.animatedValue as Float
+ val surfaceHeight: Float =
+ target.screenSpaceBounds.height().toFloat()
+
+ unoccludeMatrix.setTranslate(
+ 0f,
+ (1f - animatedValue) *
+ surfaceHeight *
+ UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT
+ )
+
+ SurfaceParams.Builder(target.leash)
+ .withAlpha(animatedValue)
+ .withMatrix(unoccludeMatrix)
+ .withCornerRadius(windowCornerRadius)
+ .build()
+ .also { applier.scheduleApply(it) }
+ }
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ try {
+ unoccludeAnimationFinishedCallback
+ ?.onAnimationFinished()
+ unoccludeAnimator = null
+ interactionJankMonitor.end(
+ InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION
+ )
+ } catch (e: RemoteException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ )
+ start()
+ }
+ }
+ }
+
+ override fun onAnimationCancelled() {
+ Log.d(TAG, "unoccludeAnimationRunner#onAnimationCancelled")
+ context.mainExecutor.execute { unoccludeAnimator?.cancel() }
+ Log.d(TAG, "Unocclude animation cancelled.")
+ interactionJankMonitor.cancel(InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION)
+ }
+ }
+
+ /**
+ * Called when Window Manager tells the KeyguardService directly that we're occluded or not
+ * occluded, without starting an occlude/unocclude remote animation. This happens if occlusion
+ * state changes without an animation (such as if a SHOW_WHEN_LOCKED activity is launched while
+ * we're unlocked), or if an animation has been cancelled/interrupted and Window Manager wants
+ * to make sure that we're in the correct state.
+ */
+ fun onKeyguardServiceSetOccluded(occluded: Boolean) {
+ Log.d(TAG, "#onKeyguardServiceSetOccluded($occluded)")
+ keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(occluded)
+ }
+
+ @VisibleForTesting
+ val occludeAnimationController: ActivityTransitionAnimator.Controller =
+ object : ActivityTransitionAnimator.Controller {
+
+ override var transitionContainer: ViewGroup
+ get() = keyguardViewController.get().getViewRootImpl().view as ViewGroup
+ set(_) {
+ // Should never be set.
+ }
+
+ /** TODO(b/326470033): Extract this logic into ViewModels. */
+ override fun createAnimatorState(): TransitionAnimator.State {
+ val fullWidth = transitionContainer.width
+ val fullHeight = transitionContainer.height
+
+ if (
+ keyguardOcclusionInteractor.showWhenLockedActivityLaunchedFromPowerGesture.value
+ ) {
+ val initialHeight = fullHeight / 3f
+ val initialWidth = fullWidth / 3f
+
+ // Start the animation near the power button, at one-third size, since the
+ // camera was launched from the power button.
+ return TransitionAnimator.State(
+ top = (powerButtonY - initialHeight / 2f).toInt(),
+ bottom = (powerButtonY + initialHeight / 2f).toInt(),
+ left = (fullWidth - initialWidth).toInt(),
+ right = fullWidth,
+ topCornerRadius = windowCornerRadius,
+ bottomCornerRadius = windowCornerRadius,
+ )
+ } else {
+ val initialHeight = fullHeight / 2f
+ val initialWidth = fullWidth / 2f
+
+ // Start the animation in the center of the screen, scaled down to half
+ // size.
+ return TransitionAnimator.State(
+ top = (fullHeight - initialHeight).toInt() / 2,
+ bottom = (initialHeight + (fullHeight - initialHeight) / 2).toInt(),
+ left = (fullWidth - initialWidth).toInt() / 2,
+ right = (initialWidth + (fullWidth - initialWidth) / 2).toInt(),
+ topCornerRadius = windowCornerRadius,
+ bottomCornerRadius = windowCornerRadius,
+ )
+ }
+ }
+ }
+
+ private fun createInteractionJankMonitorConf(
+ cuj: Int,
+ tag: String?
+ ): InteractionJankMonitor.Configuration.Builder {
+ val builder =
+ InteractionJankMonitor.Configuration.Builder.withView(
+ cuj,
+ keyguardViewController.get().getViewRootImpl().view
+ )
+ return if (tag != null) builder.setTag(tag) else builder
+ }
+
+ companion object {
+ val TAG = "WindowManagerOcclusion"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 968c3e3a6792..5306645bf69f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -50,6 +50,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager;
+import com.android.systemui.keyguard.WindowManagerOcclusionManager;
import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthModule;
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
@@ -163,7 +164,8 @@ public interface KeyguardModule {
SystemPropertiesHelper systemPropertiesHelper,
Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
SelectedUserInteractor selectedUserInteractor,
- KeyguardInteractor keyguardInteractor) {
+ KeyguardInteractor keyguardInteractor,
+ WindowManagerOcclusionManager windowManagerOcclusionManager) {
return new KeyguardViewMediator(
context,
uiEventLogger,
@@ -209,7 +211,8 @@ public interface KeyguardModule {
systemPropertiesHelper,
wmLockscreenVisibilityManager,
selectedUserInteractor,
- keyguardInteractor);
+ keyguardInteractor,
+ windowManagerOcclusionManager);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 0ea53906b7f8..7ad5aac63837 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -59,7 +59,7 @@ interface KeyguardClockRepository {
val currentClock: StateFlow<ClockController?>
- val previewClock: StateFlow<ClockController>
+ val previewClockPair: StateFlow<Pair<ClockController, ClockController>>
val clockEventController: ClockEventController
fun setClockSize(@ClockSize size: Int)
@@ -120,13 +120,14 @@ constructor(
initialValue = clockRegistry.createCurrentClock()
)
- override val previewClock: StateFlow<ClockController> =
+ override val previewClockPair: StateFlow<Pair<ClockController, ClockController>> =
currentClockId
- .map { clockRegistry.createCurrentClock() }
+ .map { Pair(clockRegistry.createCurrentClock(), clockRegistry.createCurrentClock()) }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = clockRegistry.createCurrentClock()
+ initialValue =
+ Pair(clockRegistry.createCurrentClock(), clockRegistry.createCurrentClock())
)
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepository.kt
new file mode 100644
index 000000000000..e3654b415017
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepository.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/**
+ * Information about the SHOW_WHEN_LOCKED activity that is either newly on top of the task stack, or
+ * newly not on top of the task stack.
+ */
+data class ShowWhenLockedActivityInfo(
+ /** Whether the activity is on top. If not, we're unoccluding and will be animating it out. */
+ val isOnTop: Boolean,
+
+ /**
+ * Information about the activity, which we use for transition internals and also to customize
+ * animations.
+ */
+ val taskInfo: RunningTaskInfo? = null
+) {
+ fun isDream(): Boolean {
+ return taskInfo?.topActivityType == WindowConfiguration.ACTIVITY_TYPE_DREAM
+ }
+}
+
+/**
+ * Maintains state about "occluding" activities - activities with FLAG_SHOW_WHEN_LOCKED, which are
+ * capable of displaying over the lockscreen while the device is still locked (such as Google Maps
+ * navigation).
+ *
+ * Window Manager considers the device to be in the "occluded" state whenever such an activity is on
+ * top of the task stack, including while we're unlocked, while keyguard code considers us to be
+ * occluded only when we're locked, with an occluding activity currently displaying over the
+ * lockscreen.
+ *
+ * This dual definition is confusing, so this repository collects all of the signals WM gives us,
+ * and consolidates them into [showWhenLockedActivityInfo.isOnTop], which is the actual question WM
+ * is answering when they say whether we're 'occluded'. Keyguard then uses this signal to
+ * conditionally transition to [KeyguardState.OCCLUDED] where appropriate.
+ */
+@SysUISingleton
+class KeyguardOcclusionRepository @Inject constructor() {
+ val showWhenLockedActivityInfo = MutableStateFlow(ShowWhenLockedActivityInfo(isOnTop = false))
+
+ /**
+ * Sets whether there's a SHOW_WHEN_LOCKED activity on top of the task stack, and optionally,
+ * information about the activity itself.
+ *
+ * If no value is provided for [taskInfo], we'll default to the current [taskInfo].
+ *
+ * The [taskInfo] is always present when this method is called from the occlude/unocclude
+ * animation runners. We use the default when calling from [KeyguardService.isOccluded], since
+ * we only receive a true/false value there. isOccluded is mostly redundant - it's almost always
+ * called with true after an occlusion animation has started, and with false after an unocclude
+ * animation has started. In those cases, we don't want to clear out the taskInfo just because
+ * it wasn't available at that call site.
+ */
+ fun setShowWhenLockedActivityInfo(
+ onTop: Boolean,
+ taskInfo: RunningTaskInfo? = showWhenLockedActivityInfo.value.taskInfo
+ ) {
+ showWhenLockedActivityInfo.value =
+ ShowWhenLockedActivityInfo(
+ isOnTop = onTop,
+ taskInfo = taskInfo,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 64e28700aa74..3a6423d680c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -95,6 +95,7 @@ interface KeyguardRepository {
val isKeyguardShowing: Flow<Boolean>
/** Is an activity showing over the keyguard? */
+ @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED")
val isKeyguardOccluded: Flow<Boolean>
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 9b3f13d16911..d9479de7e25b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -75,7 +75,6 @@ constructor(
powerInteractor: PowerInteractor,
private val scrimLogger: ScrimLogger,
) : LightRevealScrimRepository {
-
companion object {
val TAG = LightRevealScrimRepository::class.simpleName!!
}
@@ -156,7 +155,11 @@ constructor(
override fun startRevealAmountAnimator(reveal: Boolean) {
if (reveal == willBeOrIsRevealed) return
willBeOrIsRevealed = reveal
- if (reveal) revealAmountAnimator.start() else revealAmountAnimator.reverse()
+ if (reveal && !revealAmountAnimator.isRunning) {
+ revealAmountAnimator.start()
+ } else {
+ revealAmountAnimator.reverse()
+ }
scrimLogger.d(TAG, "startRevealAmountAnimator, reveal: ", reveal)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 9fc36923b04d..88ddfd4f4347 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -21,6 +21,7 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -31,8 +32,8 @@ import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.flow.sample
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@SysUISingleton
@@ -46,13 +47,16 @@ constructor(
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
private val communalInteractor: CommunalInteractor,
- private val powerInteractor: PowerInteractor,
+ powerInteractor: PowerInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.ALTERNATE_BOUNCER,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
@@ -62,7 +66,6 @@ constructor(
listenForTransitionToCamera(scope, keyguardInteractor)
}
- @FlowPreview
private fun listenForAlternateBouncerToLockscreenHubAodOrDozing() {
scope.launch {
keyguardInteractor.alternateBouncerShowing
@@ -71,7 +74,7 @@ constructor(
// happening prematurely.
// This should eventually be removed in favor of
// [KeyguardTransitionInteractor#startDismissKeyguardTransition]
- .sample(150L)
+ .onEach { delay(150L) }
.sampleCombine(
keyguardInteractor.primaryBouncerShowing,
startedKeyguardTransitionStep,
@@ -113,6 +116,11 @@ constructor(
}
private fun listenForAlternateBouncerToGone() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ // Handled via #dismissAlternateBouncer.
+ return
+ }
+
scope.launch {
keyguardInteractor.isKeyguardGoingAway
.sampleUtil(finishedKeyguardState, ::Pair)
@@ -150,6 +158,10 @@ constructor(
}
}
+ fun dismissAlternateBouncer() {
+ scope.launch { startTransitionTo(KeyguardState.GONE) }
+ }
+
companion object {
const val TAG = "FromAlternateBouncerTransitionInteractor"
val TRANSITION_DURATION_MS = 300.milliseconds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index e0b5c0e1f4c6..9040e031d54e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -27,12 +27,17 @@ import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
+import java.util.UUID
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@SysUISingleton
@@ -45,67 +50,126 @@ constructor(
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
+ powerInteractor: PowerInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.AOD,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
- listenForAodToLockscreen()
+ listenForAodToAwake()
+ listenForAodToOccluded()
listenForAodToPrimaryBouncer()
listenForAodToGone()
- listenForAodToOccluded()
listenForTransitionToCamera(scope, keyguardInteractor)
}
/**
+ * Listen for the signal that we're waking up and figure what state we need to transition to.
+ */
+ private fun listenForAodToAwake() {
+ val transitionToLockscreen: suspend (TransitionStep) -> UUID? =
+ { startedStep: TransitionStep ->
+ val modeOnCanceled =
+ if (startedStep.from == KeyguardState.LOCKSCREEN) {
+ TransitionModeOnCanceled.REVERSE
+ } else if (startedStep.from == KeyguardState.GONE) {
+ TransitionModeOnCanceled.RESET
+ } else {
+ TransitionModeOnCanceled.LAST_VALUE
+ }
+ startTransitionTo(
+ toState = KeyguardState.LOCKSCREEN,
+ modeOnCanceled = modeOnCanceled,
+ )
+ }
+
+ if (KeyguardWmStateRefactor.isEnabled) {
+ // The refactor uses PowerInteractor's wakefulness, which is the earliest wake signal
+ // available. We have all of the information we need at this time to make a decision
+ // about where to transition.
+ scope.launch {
+ powerInteractor.detailedWakefulness
+ // React only to wake events.
+ .filter { it.isAwake() }
+ .sample(
+ startedKeyguardTransitionStep,
+ keyguardInteractor.biometricUnlockState,
+ keyguardInteractor.primaryBouncerShowing,
+ )
+ // Make sure we've at least STARTED a transition to AOD.
+ .filter { (_, startedStep, _, _) -> startedStep.to == KeyguardState.AOD }
+ .collect { (_, startedStep, biometricUnlockState, primaryBouncerShowing) ->
+ // Check with the superclass to see if an occlusion transition is needed.
+ // Also, don't react to wake and unlock events, as we'll be receiving a call
+ // to #dismissAod() shortly when the authentication completes.
+ if (
+ !maybeStartTransitionToOccludedOrInsecureCamera() &&
+ !isWakeAndUnlock(biometricUnlockState) &&
+ !primaryBouncerShowing
+ ) {
+ transitionToLockscreen(startedStep)
+ }
+ }
+ }
+ } else {
+ scope.launch {
+ keyguardInteractor
+ .dozeTransitionTo(DozeStateModel.FINISH)
+ .sample(
+ keyguardInteractor.isKeyguardShowing,
+ startedKeyguardTransitionStep,
+ keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.biometricUnlockState,
+ keyguardInteractor.primaryBouncerShowing,
+ )
+ .collect {
+ (
+ _,
+ isKeyguardShowing,
+ lastStartedStep,
+ occluded,
+ biometricUnlockState,
+ primaryBouncerShowing) ->
+ if (
+ lastStartedStep.to == KeyguardState.AOD &&
+ !occluded &&
+ !isWakeAndUnlock(biometricUnlockState) &&
+ isKeyguardShowing &&
+ !primaryBouncerShowing
+ ) {
+ transitionToLockscreen(lastStartedStep)
+ }
+ }
+ }
+ }
+ }
+
+ /**
* There are cases where the transition to AOD begins but never completes, such as tapping power
* during an incoming phone call when unlocked. In this case, GONE->AOD should be interrupted to
* run AOD->OCCLUDED.
*/
private fun listenForAodToOccluded() {
- scope.launch {
- keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect {
- (isOccluded, startedKeyguardState) ->
- if (isOccluded && startedKeyguardState == KeyguardState.AOD) {
- startTransitionTo(
- toState = KeyguardState.OCCLUDED,
- modeOnCanceled = TransitionModeOnCanceled.RESET
- )
- }
- }
+ if (KeyguardWmStateRefactor.isEnabled) {
+ // Handled by calls to maybeStartTransitionToOccludedOrInsecureCamera on waking.
+ return
}
- }
- private fun listenForAodToLockscreen() {
scope.launch {
- keyguardInteractor
- .dozeTransitionTo(DozeStateModel.FINISH)
- .sample(
- startedKeyguardTransitionStep,
- keyguardInteractor.isKeyguardOccluded,
- keyguardInteractor.biometricUnlockState,
- )
- .collect { (_, lastStartedStep, occluded, biometricUnlockState) ->
- if (
- lastStartedStep.to == KeyguardState.AOD &&
- !occluded &&
- !isWakeAndUnlock(biometricUnlockState)
- ) {
- val modeOnCanceled =
- if (lastStartedStep.from == KeyguardState.LOCKSCREEN) {
- TransitionModeOnCanceled.REVERSE
- } else if (lastStartedStep.from == KeyguardState.GONE) {
- TransitionModeOnCanceled.RESET
- } else {
- TransitionModeOnCanceled.LAST_VALUE
- }
+ keyguardInteractor.isKeyguardOccluded
+ .sample(startedKeyguardTransitionStep, ::Pair)
+ .collect { (isOccluded, lastStartedStep) ->
+ if (isOccluded && lastStartedStep.to == KeyguardState.AOD) {
startTransitionTo(
- toState = KeyguardState.LOCKSCREEN,
- modeOnCanceled = modeOnCanceled,
+ toState = KeyguardState.OCCLUDED,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
)
}
}
@@ -130,17 +194,36 @@ constructor(
private fun listenForAodToGone() {
if (KeyguardWmStateRefactor.isEnabled) {
+ // Handled via #dismissAod.
return
}
scope.launch {
- keyguardInteractor.biometricUnlockState.sample(finishedKeyguardState, ::Pair).collect {
- (biometricUnlockState, keyguardState) ->
- KeyguardWmStateRefactor.assertInLegacyMode()
- if (keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)) {
- startTransitionTo(KeyguardState.GONE)
+ powerInteractor.isAwake
+ .debounce(50L)
+ .sample(
+ keyguardInteractor.biometricUnlockState,
+ startedKeyguardTransitionStep,
+ keyguardInteractor.isKeyguardShowing,
+ keyguardInteractor.isKeyguardDismissible,
+ )
+ .collect {
+ (
+ isAwake,
+ biometricUnlockState,
+ lastStartedTransitionStep,
+ isKeyguardShowing,
+ isKeyguardDismissible) ->
+ KeyguardWmStateRefactor.assertInLegacyMode()
+ if (
+ isAwake &&
+ lastStartedTransitionStep.to == KeyguardState.AOD &&
+ (isWakeAndUnlock(biometricUnlockState) ||
+ (!isKeyguardShowing && isKeyguardDismissible))
+ ) {
+ startTransitionTo(KeyguardState.GONE)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 54d5908e9fa4..57b2a632008a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -22,17 +22,20 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.toQuad
-import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.kotlin.Utils.Companion.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@SysUISingleton
@@ -45,39 +48,116 @@ constructor(
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
- private val powerInteractor: PowerInteractor,
+ powerInteractor: PowerInteractor,
private val communalInteractor: CommunalInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.DOZING,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
- listenForDozingToLockscreenHubOrOccluded()
- listenForDozingToGone()
+ listenForDozingToAny()
+ listenForWakeFromDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
}
- private fun listenForDozingToLockscreenHubOrOccluded() {
+ private val canDismissLockScreen: Flow<Boolean> =
+ combine(
+ keyguardInteractor.isKeyguardShowing,
+ keyguardInteractor.isKeyguardDismissible,
+ ) { isKeyguardShowing, isKeyguardDismissible ->
+ isKeyguardDismissible && !isKeyguardShowing
+ }
+
+ private fun listenForDozingToAny() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
scope.launch {
powerInteractor.isAwake
+ .debounce(50L)
+ .sample(
+ keyguardInteractor.biometricUnlockState,
+ startedKeyguardTransitionStep,
+ keyguardInteractor.isKeyguardOccluded,
+ communalInteractor.isIdleOnCommunal,
+ canDismissLockScreen,
+ keyguardInteractor.primaryBouncerShowing,
+ )
+ .collect {
+ (
+ isAwake,
+ biometricUnlockState,
+ lastStartedTransition,
+ occluded,
+ isIdleOnCommunal,
+ canDismissLockScreen,
+ primaryBouncerShowing) ->
+ if (!(isAwake && lastStartedTransition.to == KeyguardState.DOZING)) {
+ return@collect
+ }
+ startTransitionTo(
+ if (isWakeAndUnlock(biometricUnlockState)) {
+ KeyguardState.GONE
+ } else if (canDismissLockScreen) {
+ KeyguardState.GONE
+ } else if (primaryBouncerShowing) {
+ KeyguardState.PRIMARY_BOUNCER
+ } else if (occluded) {
+ KeyguardState.OCCLUDED
+ } else if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ )
+ }
+ }
+ }
+
+ /** Figure out what state to transition to when we awake from DOZING. */
+ private fun listenForWakeFromDozing() {
+ if (!KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
+ scope.launch {
+ powerInteractor.detailedWakefulness
+ .filter { it.isAwake() }
.sample(
- combine(
- startedKeyguardTransitionStep,
- keyguardInteractor.isKeyguardOccluded,
- communalInteractor.isIdleOnCommunal,
- ::Triple
- ),
- ::toQuad
+ startedKeyguardTransitionStep,
+ communalInteractor.isIdleOnCommunal,
+ keyguardInteractor.biometricUnlockState,
+ canDismissLockScreen,
+ keyguardInteractor.primaryBouncerShowing,
)
- .collect { (isAwake, lastStartedTransition, occluded, isIdleOnCommunal) ->
- if (isAwake && lastStartedTransition.to == KeyguardState.DOZING) {
+ // If we haven't at least STARTED a transition to DOZING, ignore.
+ .filter { (_, startedStep, _, _) -> startedStep.to == KeyguardState.DOZING }
+ .collect {
+ (
+ _,
+ _,
+ isIdleOnCommunal,
+ biometricUnlockState,
+ canDismissLockscreen,
+ primaryBouncerShowing) ->
+ if (
+ !maybeStartTransitionToOccludedOrInsecureCamera() &&
+ // Handled by dismissFromDozing().
+ !isWakeAndUnlock(biometricUnlockState)
+ ) {
startTransitionTo(
- if (occluded) {
- KeyguardState.OCCLUDED
+ if (canDismissLockscreen) {
+ KeyguardState.GONE
+ } else if (primaryBouncerShowing) {
+ KeyguardState.PRIMARY_BOUNCER
} else if (isIdleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
} else {
@@ -89,19 +169,9 @@ constructor(
}
}
- private fun listenForDozingToGone() {
- scope.launch {
- keyguardInteractor.biometricUnlockState
- .sample(startedKeyguardTransitionStep, ::Pair)
- .collect { (biometricUnlockState, lastStartedTransition) ->
- if (
- lastStartedTransition.to == KeyguardState.DOZING &&
- isWakeAndUnlock(biometricUnlockState)
- ) {
- startTransitionTo(KeyguardState.GONE)
- }
- }
- }
+ /** Dismisses keyguard from the DOZING state. */
+ fun dismissFromDozing() {
+ scope.launch { startTransitionTo(KeyguardState.GONE) }
}
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index a6cdaa8c6761..6433d0ede796 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -25,6 +25,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -46,12 +47,16 @@ constructor(
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
+ powerInteractor: PowerInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index acfa107cc1f1..31371384e338 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -23,10 +23,14 @@ import com.android.systemui.Flags.communalHub
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.util.kotlin.Utils
+import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -34,6 +38,7 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@SysUISingleton
@@ -47,17 +52,23 @@ constructor(
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
private val glanceableHubTransitions: GlanceableHubTransitions,
+ powerInteractor: PowerInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.DREAMING,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
listenForDreamingToOccluded()
- listenForDreamingToGone()
+ listenForDreamingToGoneWhenDismissable()
+ listenForDreamingToGoneFromBiometricUnlock()
+ listenForDreamingToLockscreen()
listenForDreamingToAodOrDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
listenForDreamingToGlanceableHub()
@@ -76,6 +87,7 @@ constructor(
fun startToLockscreenTransition() {
scope.launch {
+ KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode()
if (
transitionInteractor.startedKeyguardState.replayCache.last() ==
KeyguardState.DREAMING
@@ -86,22 +98,80 @@ constructor(
}
private fun listenForDreamingToOccluded() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ combine(
+ keyguardInteractor.isDreaming,
+ keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop,
+ ::Pair
+ )
+ .sample(startedKeyguardTransitionStep, ::toTriple)
+ .filter { (isDreaming, _, startedStep) ->
+ !isDreaming && startedStep.to == KeyguardState.DREAMING
+ }
+ .collect { maybeStartTransitionToOccludedOrInsecureCamera() }
+ }
+ } else {
+ scope.launch {
+ combine(
+ keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.isDreaming,
+ ::Pair
+ )
+ .sample(startedKeyguardTransitionStep, Utils.Companion::toTriple)
+ .collect { (isOccluded, isDreaming, lastStartedTransition) ->
+ if (
+ isOccluded &&
+ !isDreaming &&
+ lastStartedTransition.to == KeyguardState.DREAMING
+ ) {
+ startTransitionTo(KeyguardState.OCCLUDED)
+ }
+ }
+ }
+ }
+ }
+
+ private fun listenForDreamingToLockscreen() {
+ if (!KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
+ scope.launch {
+ keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
+ .filter { onTop -> !onTop }
+ .sample(startedKeyguardState)
+ .collect { startedState ->
+ if (startedState == KeyguardState.DREAMING) {
+ startTransitionTo(KeyguardState.LOCKSCREEN)
+ }
+ }
+ }
+ }
+
+ private fun listenForDreamingToGoneWhenDismissable() {
scope.launch {
- combine(keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isDreaming, ::Pair)
- .sample(startedKeyguardTransitionStep, ::toTriple)
- .collect { (isOccluded, isDreaming, lastStartedTransition) ->
+ keyguardInteractor.isAbleToDream
+ .sampleCombine(
+ keyguardInteractor.isKeyguardShowing,
+ keyguardInteractor.isKeyguardDismissible,
+ startedKeyguardTransitionStep,
+ )
+ .collect {
+ (isDreaming, isKeyguardShowing, isKeyguardDismissible, lastStartedTransition) ->
if (
- isOccluded &&
- !isDreaming &&
- lastStartedTransition.to == KeyguardState.DREAMING
+ !isDreaming &&
+ lastStartedTransition.to == KeyguardState.DREAMING &&
+ isKeyguardDismissible &&
+ !isKeyguardShowing
) {
- startTransitionTo(KeyguardState.OCCLUDED)
+ startTransitionTo(KeyguardState.GONE)
}
}
}
}
- private fun listenForDreamingToGone() {
+ private fun listenForDreamingToGoneFromBiometricUnlock() {
scope.launch {
keyguardInteractor.biometricUnlockState
.sample(startedKeyguardTransitionStep, ::Pair)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 786c3c6697d9..51bc3ae778e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -23,6 +23,7 @@ import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -35,6 +36,7 @@ import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -49,14 +51,18 @@ constructor(
private val keyguardInteractor: KeyguardInteractor,
override val transitionRepository: KeyguardTransitionRepository,
transitionInteractor: KeyguardTransitionInteractor,
- private val powerInteractor: PowerInteractor,
+ powerInteractor: PowerInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.GLANCEABLE_HUB,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
+
override fun start() {
if (!Flags.communalHub()) {
return
@@ -151,14 +157,27 @@ constructor(
}
private fun listenForHubToOccluded() {
- scope.launch {
- and(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming))
- .sample(startedKeyguardState, ::Pair)
- .collect { (isOccludedAndNotDreaming, keyguardState) ->
- if (isOccludedAndNotDreaming && keyguardState == fromState) {
- startTransitionTo(KeyguardState.OCCLUDED)
+ if (KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
+ .filter { onTop -> onTop }
+ .sample(startedKeyguardState)
+ .collect {
+ if (it == KeyguardState.GLANCEABLE_HUB) {
+ maybeStartTransitionToOccludedOrInsecureCamera()
+ }
}
- }
+ }
+ } else {
+ scope.launch {
+ and(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming))
+ .sample(startedKeyguardState, ::Pair)
+ .collect { (isOccludedAndNotDreaming, keyguardState) ->
+ if (isOccludedAndNotDreaming && keyguardState == fromState) {
+ startTransitionTo(KeyguardState.OCCLUDED)
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 7593ac252543..d5a9bd19d766 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -22,6 +22,8 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -34,6 +36,8 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@SysUISingleton
@@ -46,14 +50,18 @@ constructor(
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
- private val powerInteractor: PowerInteractor,
+ powerInteractor: PowerInteractor,
private val communalInteractor: CommunalInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ private val biometricSettingsRepository: BiometricSettingsRepository,
) :
TransitionInteractor(
fromState = KeyguardState.GONE,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
@@ -65,23 +73,46 @@ constructor(
// Primarily for when the user chooses to lock down the device
private fun listenForGoneToLockscreenOrHub() {
- scope.launch {
- keyguardInteractor.isKeyguardShowing
- .sample(
- startedKeyguardState,
- communalInteractor.isIdleOnCommunal,
- )
- .collect { (isKeyguardShowing, startedState, isIdleOnCommunal) ->
- if (isKeyguardShowing && startedState == KeyguardState.GONE) {
- val to =
- if (isIdleOnCommunal) {
- KeyguardState.GLANCEABLE_HUB
- } else {
- KeyguardState.LOCKSCREEN
- }
- startTransitionTo(to)
+ if (KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ biometricSettingsRepository.isCurrentUserInLockdown
+ .distinctUntilChanged()
+ .filter { inLockdown -> inLockdown }
+ .sample(
+ startedKeyguardState,
+ communalInteractor.isIdleOnCommunal,
+ )
+ .collect { (_, startedState, isIdleOnCommunal) ->
+ if (startedState == KeyguardState.GONE) {
+ val to =
+ if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ startTransitionTo(to, ownerReason = "User initiated lockdown")
+ }
}
- }
+ }
+ } else {
+ scope.launch {
+ keyguardInteractor.isKeyguardShowing
+ .sample(
+ startedKeyguardState,
+ communalInteractor.isIdleOnCommunal,
+ )
+ .collect { (isKeyguardShowing, startedState, isIdleOnCommunal) ->
+ if (isKeyguardShowing && startedState == KeyguardState.GONE) {
+ val to =
+ if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ startTransitionTo(to)
+ }
+ }
+ }
}
}
@@ -122,24 +153,10 @@ constructor(
private fun listenForGoneToAodOrDozing() {
scope.launch {
- powerInteractor.isAsleep
- .sample(
- combine(
- startedKeyguardTransitionStep,
- keyguardInteractor.isAodAvailable,
- ::Pair
- ),
- ::toTriple
- )
- .collect { (isAsleep, lastStartedStep, isAodAvailable) ->
- if (lastStartedStep.to == KeyguardState.GONE && isAsleep) {
- startTransitionTo(
- toState =
- if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING,
- modeOnCanceled = TransitionModeOnCanceled.RESET,
- )
- }
- }
+ listenForSleepTransition(
+ from = KeyguardState.GONE,
+ modeOnCanceledFromStartedStep = { TransitionModeOnCanceled.RESET },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 7263ae96b3a8..bcad3324b258 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -33,7 +33,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
-import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
@@ -44,6 +43,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -61,21 +62,24 @@ constructor(
private val keyguardInteractor: KeyguardInteractor,
private val flags: FeatureFlags,
private val shadeRepository: ShadeRepository,
- private val powerInteractor: PowerInteractor,
+ powerInteractor: PowerInteractor,
private val glanceableHubTransitions: GlanceableHubTransitions,
private val swipeToDismissInteractor: SwipeToDismissInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.LOCKSCREEN,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
listenForLockscreenToGone()
listenForLockscreenToGoneDragging()
- listenForLockscreenToOccluded()
+ listenForLockscreenToOccludedOrDreaming()
listenForLockscreenToAodOrDozing()
listenForLockscreenToPrimaryBouncer()
listenForLockscreenToDreaming()
@@ -114,6 +118,10 @@ constructor(
}
private fun listenForLockscreenToDreaming() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING)
scope.launch {
keyguardInteractor.isAbleToDream
@@ -156,7 +164,10 @@ constructor(
if (
isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN
) {
- startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+ startTransitionTo(
+ KeyguardState.PRIMARY_BOUNCER,
+ ownerReason = "#listenForLockscreenToPrimaryBouncer"
+ )
}
}
}
@@ -253,7 +264,8 @@ constructor(
transitionId =
startTransitionTo(
toState = KeyguardState.PRIMARY_BOUNCER,
- animator = null, // transition will be manually controlled
+ animator = null, // transition will be manually controlled,
+ ownerReason = "#listenForLockscreenToPrimaryBouncerDragging"
)
}
}
@@ -287,7 +299,7 @@ constructor(
if (KeyguardWmStateRefactor.isEnabled) {
// When the refactor is enabled, we no longer use isKeyguardGoingAway.
scope.launch {
- swipeToDismissInteractor.dismissFling.collect { _ ->
+ swipeToDismissInteractor.dismissFling.filterNotNull().collect { _ ->
startTransitionTo(KeyguardState.GONE)
}
}
@@ -308,47 +320,51 @@ constructor(
}
}
- private fun listenForLockscreenToOccluded() {
- scope.launch {
- keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect {
- (isOccluded, keyguardState) ->
- if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) {
- startTransitionTo(KeyguardState.OCCLUDED)
- }
+ private fun listenForLockscreenToOccludedOrDreaming() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ keyguardOcclusionInteractor.showWhenLockedActivityInfo
+ .filter { it.isOnTop }
+ .sample(startedKeyguardState, ::Pair)
+ .collect { (taskInfo, startedState) ->
+ if (startedState == KeyguardState.LOCKSCREEN) {
+ startTransitionTo(
+ if (taskInfo.isDream()) {
+ KeyguardState.DREAMING
+ } else {
+ KeyguardState.OCCLUDED
+ }
+ )
+ }
+ }
+ }
+ } else {
+ scope.launch {
+ keyguardInteractor.isKeyguardOccluded
+ .sample(startedKeyguardState, ::Pair)
+ .collect { (isOccluded, keyguardState) ->
+ if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) {
+ startTransitionTo(KeyguardState.OCCLUDED)
+ }
+ }
}
}
}
private fun listenForLockscreenToAodOrDozing() {
scope.launch {
- powerInteractor.isAsleep
- .sample(
- combine(
- startedKeyguardTransitionStep,
- keyguardInteractor.isAodAvailable,
- ::Pair
- ),
- ::toTriple
- )
- .collect { (isAsleep, lastStartedStep, isAodAvailable) ->
- if (lastStartedStep.to == KeyguardState.LOCKSCREEN && isAsleep) {
- val toState =
- if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
- val modeOnCanceled =
- if (
- toState == KeyguardState.AOD &&
- lastStartedStep.from == KeyguardState.AOD
- ) {
- TransitionModeOnCanceled.REVERSE
- } else {
- TransitionModeOnCanceled.LAST_VALUE
- }
- startTransitionTo(
- toState = toState,
- modeOnCanceled = modeOnCanceled,
- )
+ listenForSleepTransition(
+ from = KeyguardState.LOCKSCREEN,
+ modeOnCanceledFromStartedStep = { startedStep ->
+ if (
+ startedStep.to == KeyguardState.AOD && startedStep.from == KeyguardState.AOD
+ ) {
+ TransitionModeOnCanceled.REVERSE
+ } else {
+ TransitionModeOnCanceled.LAST_VALUE
}
}
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index efb604d5dadc..f10327e02240 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -22,17 +22,17 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
-import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@SysUISingleton
@@ -45,20 +45,23 @@ constructor(
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
- private val powerInteractor: PowerInteractor,
+ powerInteractor: PowerInteractor,
private val communalInteractor: CommunalInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.OCCLUDED,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
listenForOccludedToLockscreenOrHub()
listenForOccludedToDreaming()
- listenForOccludedToAodOrDozing()
+ listenForOccludedToAsleep()
listenForOccludedToGone()
listenForOccludedToAlternateBouncer()
listenForOccludedToPrimaryBouncer()
@@ -90,43 +93,72 @@ constructor(
}
private fun listenForOccludedToLockscreenOrHub() {
- scope.launch {
- keyguardInteractor.isKeyguardOccluded
- .sample(
- keyguardInteractor.isKeyguardShowing,
- startedKeyguardTransitionStep,
- communalInteractor.isIdleOnCommunal,
- )
- .collect { (isOccluded, isShowing, lastStartedKeyguardState, isIdleOnCommunal) ->
- // Occlusion signals come from the framework, and should interrupt any
- // existing transition
- if (
- !isOccluded &&
- isShowing &&
- lastStartedKeyguardState.to == KeyguardState.OCCLUDED
- ) {
- val to =
- if (isIdleOnCommunal) {
- KeyguardState.GLANCEABLE_HUB
- } else {
- KeyguardState.LOCKSCREEN
- }
- startTransitionTo(to)
+ if (KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
+ .filter { onTop -> !onTop }
+ .sample(
+ startedKeyguardState,
+ communalInteractor.isIdleOnCommunal,
+ )
+ .collect { (_, startedState, isIdleOnCommunal) ->
+ // Occlusion signals come from the framework, and should interrupt any
+ // existing transition
+ if (startedState == KeyguardState.OCCLUDED) {
+ val to =
+ if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ startTransitionTo(to)
+ }
}
- }
+ }
+ } else {
+ scope.launch {
+ keyguardInteractor.isKeyguardOccluded
+ .sample(
+ keyguardInteractor.isKeyguardShowing,
+ startedKeyguardTransitionStep,
+ communalInteractor.isIdleOnCommunal,
+ )
+ .collect { (isOccluded, isShowing, lastStartedKeyguardState, isIdleOnCommunal)
+ ->
+ // Occlusion signals come from the framework, and should interrupt any
+ // existing transition
+ if (
+ !isOccluded &&
+ isShowing &&
+ lastStartedKeyguardState.to == KeyguardState.OCCLUDED
+ ) {
+ val to =
+ if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ startTransitionTo(to)
+ }
+ }
+ }
}
}
private fun listenForOccludedToGone() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ // We don't think OCCLUDED to GONE is possible. You should always have to go via a
+ // *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard
+ // dismisses it, and even "extend unlock" doesn't unlock the device in the background.
+ // If we're wrong - sorry, add it back here.
+ return
+ }
+
scope.launch {
keyguardInteractor.isKeyguardOccluded
.sample(
- combine(
- keyguardInteractor.isKeyguardShowing,
- startedKeyguardTransitionStep,
- ::Pair
- ),
- ::toTriple
+ keyguardInteractor.isKeyguardShowing,
+ startedKeyguardTransitionStep,
)
.collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
// Occlusion signals come from the framework, and should interrupt any
@@ -142,25 +174,12 @@ constructor(
}
}
- private fun listenForOccludedToAodOrDozing() {
- scope.launch {
- powerInteractor.isAsleep
- .sample(
- combine(
- startedKeyguardTransitionStep,
- keyguardInteractor.isAodAvailable,
- ::Pair
- ),
- ::toTriple
- )
- .collect { (isAsleep, lastStartedStep, isAodAvailable) ->
- if (lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep) {
- startTransitionTo(
- if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
- )
- }
- }
- }
+ fun dismissToGone() {
+ scope.launch { startTransitionTo(KeyguardState.GONE) }
+ }
+
+ private fun listenForOccludedToAsleep() {
+ scope.launch { listenForSleepTransition(from = KeyguardState.OCCLUDED) }
}
private fun listenForOccludedToAlternateBouncer() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index c5a28463bf7e..391dccc7f444 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -31,7 +31,6 @@ import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
-import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import com.android.wm.shell.animation.Interpolators
@@ -42,6 +41,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -59,18 +59,21 @@ constructor(
private val flags: FeatureFlags,
private val keyguardSecurityModel: KeyguardSecurityModel,
private val selectedUserInteractor: SelectedUserInteractor,
- private val powerInteractor: PowerInteractor,
+ powerInteractor: PowerInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.PRIMARY_BOUNCER,
transitionInteractor = transitionInteractor,
mainDispatcher = mainDispatcher,
bgDispatcher = bgDispatcher,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
) {
override fun start() {
listenForPrimaryBouncerToGone()
- listenForPrimaryBouncerToAodOrDozing()
+ listenForPrimaryBouncerToAsleep()
listenForPrimaryBouncerToLockscreenHubOrOccluded()
listenForPrimaryBouncerToDreamingLockscreenHosted()
listenForTransitionToCamera(scope, keyguardInteractor)
@@ -128,74 +131,88 @@ constructor(
}
private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() {
- scope.launch {
- keyguardInteractor.primaryBouncerShowing
- .sample(
- powerInteractor.isAwake,
- startedKeyguardTransitionStep,
- keyguardInteractor.isKeyguardOccluded,
- keyguardInteractor.isDreaming,
- keyguardInteractor.isActiveDreamLockscreenHosted,
- communalInteractor.isIdleOnCommunal,
- )
- .collect {
- (
- isBouncerShowing,
- isAwake,
- lastStartedTransitionStep,
- occluded,
- isDreaming,
- isActiveDreamLockscreenHosted,
- isIdleOnCommunal) ->
- if (
- !isBouncerShowing &&
- lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
- isAwake &&
- !isActiveDreamLockscreenHosted
- ) {
- val toState =
- if (occluded && !isDreaming) {
- KeyguardState.OCCLUDED
- } else if (isIdleOnCommunal) {
- KeyguardState.GLANCEABLE_HUB
- } else if (isDreaming) {
- KeyguardState.DREAMING
- } else {
- KeyguardState.LOCKSCREEN
- }
- startTransitionTo(toState)
+ if (KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ keyguardInteractor.primaryBouncerShowing
+ .sample(
+ startedKeyguardTransitionStep,
+ powerInteractor.isAwake,
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ communalInteractor.isIdleOnCommunal
+ )
+ .filter { (_, startedStep, _, _) ->
+ startedStep.to == KeyguardState.PRIMARY_BOUNCER
}
- }
- }
- }
-
- private fun listenForPrimaryBouncerToAodOrDozing() {
- scope.launch {
- keyguardInteractor.primaryBouncerShowing
- .sample(
- combine(
- powerInteractor.isAsleep,
+ .collect {
+ (
+ isBouncerShowing,
+ _,
+ isAwake,
+ isActiveDreamLockscreenHosted,
+ isIdleOnCommunal) ->
+ if (
+ !maybeStartTransitionToOccludedOrInsecureCamera() &&
+ !isBouncerShowing &&
+ isAwake &&
+ !isActiveDreamLockscreenHosted
+ ) {
+ val toState =
+ if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ startTransitionTo(toState)
+ }
+ }
+ }
+ } else {
+ scope.launch {
+ keyguardInteractor.primaryBouncerShowing
+ .sample(
+ powerInteractor.isAwake,
startedKeyguardTransitionStep,
- keyguardInteractor.isAodAvailable,
- ::Triple
- ),
- ::toQuad
- )
- .collect { (isBouncerShowing, isAsleep, lastStartedTransitionStep, isAodAvailable)
- ->
- if (
- !isBouncerShowing &&
- lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
- isAsleep
- ) {
- startTransitionTo(
- if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
- )
+ keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.isDreaming,
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ communalInteractor.isIdleOnCommunal,
+ )
+ .collect {
+ (
+ isBouncerShowing,
+ isAwake,
+ lastStartedTransitionStep,
+ occluded,
+ isDreaming,
+ isActiveDreamLockscreenHosted,
+ isIdleOnCommunal) ->
+ if (
+ !isBouncerShowing &&
+ lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
+ isAwake &&
+ !isActiveDreamLockscreenHosted
+ ) {
+ val toState =
+ if (occluded && !isDreaming) {
+ KeyguardState.OCCLUDED
+ } else if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else if (isDreaming) {
+ KeyguardState.DREAMING
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ startTransitionTo(toState)
+ }
}
- }
+ }
}
}
+ private fun listenForPrimaryBouncerToAsleep() {
+ scope.launch { listenForSleepTransition(from = KeyguardState.PRIMARY_BOUNCER) }
+ }
+
private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
scope.launch {
keyguardInteractor.primaryBouncerShowing
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index 6cb1eb493db3..744301019dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -20,7 +20,7 @@ import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalTransitionProgress
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -55,9 +55,9 @@ constructor(
) {
val toScene =
if (fromState == KeyguardState.GLANCEABLE_HUB) {
- CommunalSceneKey.Blank
+ CommunalScenes.Blank
} else {
- CommunalSceneKey.Communal
+ CommunalScenes.Communal
}
var transitionId: UUID? = null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index 196770ae2f9a..2cf91563b3e4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -44,7 +44,8 @@ constructor(
val currentClock: StateFlow<ClockController?> = keyguardClockRepository.currentClock
- val previewClock: StateFlow<ClockController> = keyguardClockRepository.previewClock
+ val previewClockPair: StateFlow<Pair<ClockController, ClockController>> =
+ keyguardClockRepository.previewClockPair
var clock: ClockController? by keyguardClockRepository.clockEventController::clock
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 78749ead7ef9..f321bd7e13a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -43,7 +43,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
@@ -163,15 +163,18 @@ constructor(
.distinctUntilChanged()
/** Whether the keyguard is showing or not. */
+ @Deprecated("Use KeyguardTransitionInteractor + KeyguardState")
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
/** Whether the keyguard is dismissible or not. */
val isKeyguardDismissible: Flow<Boolean> = repository.isKeyguardDismissible
/** Whether the keyguard is occluded (covered by an activity). */
+ @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED")
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
/** Whether the keyguard is going away. */
+ @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.GONE")
val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
/** Keyguard can be clipped at the top as the shade is dragged */
@@ -292,7 +295,7 @@ constructor(
sceneInteractorProvider
.get()
.transitioningTo
- .map { it == SceneKey.Lockscreen }
+ .map { it == Scenes.Lockscreen }
.distinctUntilChanged()
.flatMapLatest { isTransitioningToLockscreenScene ->
if (isTransitioningToLockscreenScene) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
new file mode 100644
index 000000000000..9aa2202b4100
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.app.ActivityManager.RunningTaskInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardOcclusionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Logic related to keyguard occlusion. The keyguard is occluded when an activity with
+ * FLAG_SHOW_WHEN_LOCKED is on top of the activity task stack, with that activity displaying on top
+ * of ("occluding") the lockscreen UI. Common examples of this are Google Maps Navigation and the
+ * secure camera.
+ *
+ * This should usually be used only by keyguard internal classes. Most System UI use cases should
+ * use [KeyguardTransitionInteractor] to see if we're in [KeyguardState.OCCLUDED] instead.
+ */
+@SysUISingleton
+class KeyguardOcclusionInteractor
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ val repository: KeyguardOcclusionRepository,
+ val powerInteractor: PowerInteractor,
+ val transitionInteractor: KeyguardTransitionInteractor,
+ val keyguardInteractor: KeyguardInteractor,
+) {
+ val showWhenLockedActivityInfo = repository.showWhenLockedActivityInfo.asStateFlow()
+
+ /**
+ * Whether a SHOW_WHEN_LOCKED activity is on top of the task stack. This does not necessarily
+ * mean we're OCCLUDED, as we could be GONE (unlocked), with an activity that can (but is not
+ * currently) displaying over the lockscreen.
+ *
+ * Transition interactors use this to determine when we should transition to the OCCLUDED state.
+ *
+ * Outside of the transition/occlusion interactors, you almost certainly don't want to use this.
+ * Instead, use KeyguardTransitionInteractor to figure out if we're in KeyguardState.OCCLUDED.
+ */
+ val isShowWhenLockedActivityOnTop = showWhenLockedActivityInfo.map { it.isOnTop }
+
+ /** Whether we should start a transition due to the power button launch gesture. */
+ fun shouldTransitionFromPowerButtonGesture(): Boolean {
+ // powerButtonLaunchGestureTriggered remains true while we're awake from a power button
+ // gesture. Check that we were asleep or transitioning to asleep before starting a
+ // transition, to ensure we don't transition while moving between, for example,
+ // *_BOUNCER -> LOCKSCREEN.
+ return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered &&
+ KeyguardState.deviceIsAsleepInState(transitionInteractor.getStartedState())
+ }
+
+ /**
+ * Whether the SHOW_WHEN_LOCKED activity was launched from the double tap power button gesture.
+ * This remains true while the activity is running and emits false once it is killed.
+ */
+ val showWhenLockedActivityLaunchedFromPowerGesture =
+ merge(
+ // Emit true when the power launch gesture is triggered, since this means a
+ // SHOW_WHEN_LOCKED activity will be launched from the gesture (unless we're
+ // currently
+ // GONE, in which case we're going back to GONE and launching the insecure camera).
+ powerInteractor.detailedWakefulness
+ .sample(transitionInteractor.currentKeyguardState, ::Pair)
+ .map { (wakefulness, currentKeyguardState) ->
+ wakefulness.powerButtonLaunchGestureTriggered &&
+ currentKeyguardState != KeyguardState.GONE
+ },
+ // Emit false once that activity goes away.
+ isShowWhenLockedActivityOnTop.filter { !it }.map { false }
+ )
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ /**
+ * Whether launching an occluding activity will automatically dismiss keyguard. This happens if
+ * the keyguard is dismissable.
+ */
+ val occludingActivityWillDismissKeyguard =
+ keyguardInteractor.isKeyguardDismissible.stateIn(scope, SharingStarted.Eagerly, false)
+
+ /**
+ * Called to let System UI know that WM says a SHOW_WHEN_LOCKED activity is on top (or no longer
+ * on top).
+ *
+ * This signal arrives from WM when a SHOW_WHEN_LOCKED activity is started or killed - it is
+ * never set directly by System UI. While we might be the reason the activity was started
+ * (launching the camera from the power button gesture), we ultimately only receive this signal
+ * once that activity starts. It's up to us to start the appropriate keyguard transitions,
+ * because that activity is going to be visible (or not) regardless.
+ */
+ fun setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop: Boolean,
+ taskInfo: RunningTaskInfo? = null
+ ) {
+ repository.setShowWhenLockedActivityInfo(showWhenLockedActivityOnTop, taskInfo)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index b0a38811cdfc..8eb1a50086c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -46,6 +46,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAfforda
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -55,6 +56,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -66,6 +68,7 @@ class KeyguardQuickAffordanceInteractor
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
+ private val shadeInteractor: ShadeInteractor,
private val lockPatternUtils: LockPatternUtils,
private val keyguardStateController: KeyguardStateController,
private val userTracker: UserTracker,
@@ -100,9 +103,10 @@ constructor(
quickAffordanceAlwaysVisible(position),
keyguardInteractor.isDozing,
keyguardInteractor.isKeyguardShowing,
+ shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(),
biometricSettingsRepository.isCurrentUserInLockdown,
- ) { affordance, isDozing, isKeyguardShowing, isUserInLockdown ->
- if (!isDozing && isKeyguardShowing && !isUserInLockdown) {
+ ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown ->
+ if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) {
affordance
} else {
KeyguardQuickAffordanceModel.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index a03fa38ec850..c28e49db9d37 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -21,6 +21,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -37,6 +39,9 @@ constructor(
private val keyguardInteractor: KeyguardInteractor,
private val logger: KeyguardLogger,
private val powerInteractor: PowerInteractor,
+ private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val shadeInteractor: ShadeInteractor,
+ private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) {
fun start() {
@@ -47,6 +52,30 @@ constructor(
}
scope.launch {
+ sharedNotificationContainerViewModel
+ .getMaxNotifications { height, useExtraShelfSpace -> height.toInt() }
+ .collect { logger.log(TAG, VERBOSE, "Notif: max height in px", it) }
+ }
+
+ scope.launch {
+ sharedNotificationContainerViewModel.isOnLockscreen.collect {
+ logger.log(TAG, VERBOSE, "Notif: isOnLockscreen", it)
+ }
+ }
+
+ scope.launch {
+ shadeInteractor.isUserInteracting.collect {
+ logger.log(TAG, VERBOSE, "Shade: isUserInteracting", it)
+ }
+ }
+
+ scope.launch {
+ sharedNotificationContainerViewModel.isOnLockscreenWithoutShade.collect {
+ logger.log(TAG, VERBOSE, "Notif: isOnLockscreenWithoutShade", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.primaryBouncerShowing.collect {
logger.log(TAG, VERBOSE, "Primary bouncer showing", it)
}
@@ -63,6 +92,12 @@ constructor(
}
scope.launch {
+ keyguardInteractor.isKeyguardDismissible.collect {
+ logger.log(TAG, VERBOSE, "isKeyguardDismissable", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.isAbleToDream.collect {
logger.log(TAG, VERBOSE, "isAbleToDream", it)
}
@@ -75,6 +110,18 @@ constructor(
}
scope.launch {
+ keyguardInteractor.isKeyguardDismissible.collect {
+ logger.log(TAG, VERBOSE, "isDismissible", it)
+ }
+ }
+
+ scope.launch {
+ keyguardInteractor.isKeyguardShowing.collect {
+ logger.log(TAG, VERBOSE, "isShowing", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.dozeTransitionModel.collect {
logger.log(TAG, VERBOSE, "Doze transition", it)
}
@@ -85,5 +132,11 @@ constructor(
logger.log(TAG, VERBOSE, "onCameraLaunchDetected", it)
}
}
+
+ scope.launch {
+ keyguardOcclusionInteractor.showWhenLockedActivityInfo.collect {
+ logger.log(TAG, VERBOSE, "showWhenLockedActivityInfo", it)
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 719edd7e8535..00902b419a97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
@@ -35,17 +36,20 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
/** Encapsulates business-logic related to the keyguard transitions. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -54,11 +58,15 @@ class KeyguardTransitionInteractor
@Inject
constructor(
@Application val scope: CoroutineScope,
+ private val keyguardRepository: KeyguardRepository,
private val repository: KeyguardTransitionRepository,
private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>,
private val fromPrimaryBouncerTransitionInteractor:
dagger.Lazy<FromPrimaryBouncerTransitionInteractor>,
private val fromAodTransitionInteractor: dagger.Lazy<FromAodTransitionInteractor>,
+ private val fromAlternateBouncerTransitionInteractor:
+ dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
+ private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
) {
private val TAG = this::class.simpleName
@@ -205,6 +213,27 @@ constructor(
.map { step -> step.to }
.shareIn(scope, SharingStarted.Eagerly, replay = 1)
+ /** Which keyguard state to use when the device goes to sleep. */
+ val asleepKeyguardState: StateFlow<KeyguardState> =
+ keyguardRepository.isAodAvailable
+ .map { aodAvailable -> if (aodAvailable) AOD else DOZING }
+ .stateIn(scope, SharingStarted.Eagerly, DOZING)
+
+ /**
+ * A pair of the most recent STARTED step, and the transition step immediately preceding it. The
+ * transition framework enforces that the previous step is either a CANCELED or FINISHED step,
+ * and that the previous step was *to* the state the STARTED step is *from*.
+ *
+ * This flow can be used to access the previous step to determine whether it was CANCELED or
+ * FINISHED. In the case of a CANCELED step, we can also figure out which state we were coming
+ * from when we were canceled.
+ */
+ val startedStepWithPrecedingStep =
+ transitions
+ .pairwise()
+ .filter { it.newValue.transitionState == TransitionState.STARTED }
+ .stateIn(scope, SharingStarted.Eagerly, null)
+
/**
* The last [KeyguardState] to which we [TransitionState.FINISHED] a transition.
*
@@ -351,7 +380,10 @@ constructor(
when (val startedState = startedKeyguardState.replayCache.last()) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
+ ALTERNATE_BOUNCER ->
+ fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer()
AOD -> fromAodTransitionInteractor.get().dismissAod()
+ DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing()
else ->
Log.e(
"KeyguardTransitionInteractor",
@@ -404,12 +436,17 @@ constructor(
fromStatePredicate: (KeyguardState) -> Boolean,
toStatePredicate: (KeyguardState) -> Boolean,
): Flow<Boolean> {
+ return isInTransitionWhere { from, to -> fromStatePredicate(from) && toStatePredicate(to) }
+ }
+
+ fun isInTransitionWhere(
+ fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean
+ ): Flow<Boolean> {
return repository.transitions
.filter { it.transitionState != TransitionState.CANCELED }
.mapLatest {
it.transitionState != TransitionState.FINISHED &&
- fromStatePredicate(it.from) &&
- toStatePredicate(it.to)
+ fromToStatePredicate(it.from, it.to)
}
.distinctUntilChanged()
}
@@ -430,4 +467,16 @@ constructor(
*/
fun isFinishedInStateWhereValue(stateMatcher: (KeyguardState) -> Boolean) =
stateMatcher(finishedKeyguardState.replayCache.last())
+
+ fun getCurrentState(): KeyguardState {
+ return currentKeyguardState.replayCache.last()
+ }
+
+ fun getStartedState(): KeyguardState {
+ return startedKeyguardState.replayCache.last()
+ }
+
+ fun getFinishedState(): KeyguardState {
+ return finishedKeyguardState.replayCache.last()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 4d731eccd9bb..8905c9e752de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -43,7 +43,6 @@ constructor(
private val scrimLogger: ScrimLogger,
private val powerInteractor: PowerInteractor,
) {
-
init {
listenForStartedKeyguardTransitionStep()
}
@@ -52,9 +51,7 @@ constructor(
scope.launch {
transitionInteractor.startedKeyguardTransitionStep.collect {
scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it)
- lightRevealScrimRepository.startRevealAmountAnimator(
- willBeRevealedInState(it.to),
- )
+ lightRevealScrimRepository.startRevealAmountAnimator(willBeRevealedInState(it.to))
}
}
}
@@ -89,25 +86,25 @@ constructor(
companion object {
- /**
- * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
- * state after the transition is complete. If false, scrim will be fully hidden.
- */
- private fun willBeRevealedInState(state: KeyguardState): Boolean {
- return when (state) {
- KeyguardState.OFF -> false
- KeyguardState.DOZING -> false
- KeyguardState.AOD -> false
- KeyguardState.DREAMING -> true
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
- KeyguardState.GLANCEABLE_HUB -> true
- KeyguardState.ALTERNATE_BOUNCER -> true
- KeyguardState.PRIMARY_BOUNCER -> true
- KeyguardState.LOCKSCREEN -> true
- KeyguardState.GONE -> true
- KeyguardState.OCCLUDED -> true
+ /**
+ * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
+ * state after the transition is complete. If false, scrim will be fully hidden.
+ */
+ private fun willBeRevealedInState(state: KeyguardState): Boolean {
+ return when (state) {
+ KeyguardState.OFF -> false
+ KeyguardState.DOZING -> false
+ KeyguardState.AOD -> false
+ KeyguardState.DREAMING -> true
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
+ KeyguardState.GLANCEABLE_HUB -> true
+ KeyguardState.ALTERNATE_BOUNCER -> true
+ KeyguardState.PRIMARY_BOUNCER -> true
+ KeyguardState.LOCKSCREEN -> true
+ KeyguardState.GONE -> true
+ KeyguardState.OCCLUDED -> true
+ }
}
- }
val TAG = LightRevealScrimInteractor::class.simpleName!!
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 3ccbdba6d58e..375df3e8f5f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -18,15 +18,20 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.Log
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.sample
import java.util.UUID
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -46,6 +51,8 @@ sealed class TransitionInteractor(
val transitionInteractor: KeyguardTransitionInteractor,
val mainDispatcher: CoroutineDispatcher,
val bgDispatcher: CoroutineDispatcher,
+ val powerInteractor: PowerInteractor,
+ val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) {
val name = this::class.simpleName ?: "UnknownTransitionInteractor"
abstract val transitionRepository: KeyguardTransitionRepository
@@ -65,7 +72,11 @@ sealed class TransitionInteractor(
suspend fun startTransitionTo(
toState: KeyguardState,
animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
- modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE
+ modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
+ // Even more information about why the owner started this transition, if this is a dangerous
+ // transition (*cough* occlusion) where you'd be sad to not have all the info you can get in
+ // a bugreport.
+ ownerReason: String = "",
): UUID? {
if (
fromState != transitionInteractor.startedKeyguardState.replayCache.last() &&
@@ -85,7 +96,7 @@ sealed class TransitionInteractor(
return withContext(mainDispatcher) {
transitionRepository.startTransition(
TransitionInfo(
- name,
+ name + if (ownerReason.isNotBlank()) "($ownerReason)" else "",
fromState,
toState,
animator,
@@ -95,24 +106,107 @@ sealed class TransitionInteractor(
}
}
+ /**
+ * Check whether we need to transition to [KeyguardState.OCCLUDED], based on the presence of a
+ * SHOW_WHEN_LOCKED activity, or back to [KeyguardState.GONE], for some power button launch
+ * gesture cases. If so, start the transition.
+ *
+ * Returns true if a transition was started, false otherwise.
+ */
+ suspend fun maybeStartTransitionToOccludedOrInsecureCamera(): Boolean {
+ if (keyguardOcclusionInteractor.shouldTransitionFromPowerButtonGesture()) {
+ if (transitionInteractor.getCurrentState() == KeyguardState.GONE) {
+ // If the current state is GONE when the launch gesture is triggered, it means we
+ // were in transition from GONE -> DOZING/AOD due to the first power button tap. The
+ // second tap indicates that the user's intent was actually to launch the unlocked
+ // (insecure) camera, so we should transition back to GONE.
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "Power button gesture while GONE"
+ )
+ } else if (keyguardOcclusionInteractor.occludingActivityWillDismissKeyguard.value) {
+ // The double tap gesture occurred while not GONE (AOD/LOCKSCREEN/etc.), but the
+ // keyguard is dismissable. The activity launch will dismiss the keyguard, so we
+ // should transition to GONE.
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "Power button gesture on dismissable keyguard"
+ )
+ } else {
+ // Otherwise, the double tap gesture occurred while not GONE and not dismissable,
+ // which means we will launch the secure camera, which OCCLUDES the keyguard.
+ startTransitionTo(
+ KeyguardState.OCCLUDED,
+ ownerReason = "Power button gesture on lockscreen"
+ )
+ }
+
+ return true
+ } else if (keyguardOcclusionInteractor.showWhenLockedActivityInfo.value.isOnTop) {
+ // A SHOW_WHEN_LOCKED activity is on top of the task stack. Transition to OCCLUDED so
+ // it's visible.
+ // TODO(b/307976454) - Centralize transition to DREAMING here.
+ startTransitionTo(
+ KeyguardState.OCCLUDED,
+ ownerReason = "SHOW_WHEN_LOCKED activity on top"
+ )
+
+ return true
+ } else {
+ // No transition needed, let the interactor figure out where to go.
+ return false
+ }
+ }
+
+ /**
+ * Transition to the appropriate state when the device goes to sleep while in [from].
+ *
+ * We could also just use [fromState], but it's more readable in the From*TransitionInteractor
+ * if you're explicitly declaring which state you're listening from. If you passed in the wrong
+ * state, [startTransitionTo] would complain anyway.
+ */
+ suspend fun listenForSleepTransition(
+ from: KeyguardState,
+ modeOnCanceledFromStartedStep: (TransitionStep) -> TransitionModeOnCanceled = {
+ TransitionModeOnCanceled.LAST_VALUE
+ }
+ ) {
+ powerInteractor.isAsleep
+ .filter { isAsleep -> isAsleep }
+ .sample(startedKeyguardTransitionStep)
+ .filter { startedStep -> startedStep.to == from }
+ .map(modeOnCanceledFromStartedStep)
+ .collect { modeOnCanceled ->
+ startTransitionTo(
+ toState = transitionInteractor.asleepKeyguardState.value,
+ modeOnCanceled = modeOnCanceled,
+ ownerReason = "Sleep transition triggered"
+ )
+ }
+ }
+
/** This signal may come in before the occlusion signal, and can provide a custom transition */
fun listenForTransitionToCamera(
scope: CoroutineScope,
keyguardInteractor: KeyguardInteractor,
) {
- scope.launch {
- keyguardInteractor.onCameraLaunchDetected
- .sample(transitionInteractor.finishedKeyguardState)
- .collect { finishedKeyguardState ->
- // Other keyguard state transitions may trigger on the first power button push,
- // so use the last finishedKeyguardState to determine the overriding FROM state
- if (finishedKeyguardState == fromState) {
- startTransitionTo(
- toState = KeyguardState.OCCLUDED,
- modeOnCanceled = TransitionModeOnCanceled.RESET,
- )
+ if (!KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ keyguardInteractor.onCameraLaunchDetected
+ .sample(transitionInteractor.finishedKeyguardState)
+ .collect { finishedKeyguardState ->
+ // Other keyguard state transitions may trigger on the first power button
+ // push,
+ // so use the last finishedKeyguardState to determine the overriding FROM
+ // state
+ if (finishedKeyguardState == fromState) {
+ startTransitionTo(
+ toState = KeyguardState.OCCLUDED,
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
+ )
+ }
}
- }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index b81793ecec64..cff74b333530 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -19,7 +19,9 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -121,19 +123,27 @@ constructor(
* want to know if the AOD/clock/notifs/etc. are visible.
*/
val lockscreenVisibility: Flow<Boolean> =
- combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.finishedKeyguardState,
- ) { startedStep, finishedState ->
- // If we finished the transition, use the finished state. If we're running a
- // transition, use the state we're transitioning FROM. This can be different from
- // the last finished state if a transition is interrupted. For example, if we were
- // transitioning from GONE to AOD and then started AOD -> LOCKSCREEN mid-transition,
- // we want to immediately use the visibility for AOD (lockscreenVisibility=true)
- // even though the lastFinishedState is still GONE (lockscreenVisibility=false).
- if (finishedState == startedStep.to) finishedState else startedStep.from
+ transitionInteractor.currentKeyguardState
+ .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
+ .map { (currentState, startedWithPrev) ->
+ val startedFromStep = startedWithPrev?.previousValue
+ val startedStep = startedWithPrev?.newValue
+ val returningToGoneAfterCancellation =
+ startedStep?.to == KeyguardState.GONE &&
+ startedFromStep?.transitionState == TransitionState.CANCELED &&
+ startedFromStep.from == KeyguardState.GONE
+
+ if (!returningToGoneAfterCancellation) {
+ // By default, apply the lockscreen visibility of the current state.
+ KeyguardState.lockscreenVisibleInState(currentState)
+ } else {
+ // If we're transitioning to GONE after a prior canceled transition from GONE,
+ // then this is the camera launch transition from an asleep state back to GONE.
+ // We don't want to show the lockscreen since we're aborting the lock and going
+ // back to GONE.
+ KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+ }
}
- .map(KeyguardState::lockscreenVisibleInState)
.distinctUntilChanged()
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt
index 7f0b483919b3..601fbfaf1b64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.shared
import com.android.systemui.Flags
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
@@ -34,7 +33,7 @@ object ComposeLockscreen {
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.composeLockscreen() && ComposeFacade.isComposeAvailable()
+ get() = Flags.composeLockscreen()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 66fc99567d42..4812e03ec3f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -36,12 +36,13 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlin.math.max
import kotlinx.coroutines.launch
private const val TAG = "KeyguardBlueprintViewBinder"
-private const val DEBUG = true
+private const val DEBUG = false
@SysUISingleton
class KeyguardBlueprintViewBinder
@@ -127,6 +128,7 @@ constructor(
runTransition(constraintLayout, transition, Config.DEFAULT) {
// Add and remove views of sections that are not contained by the other.
blueprint.replaceViews(prevBluePrint, constraintLayout)
+ logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
@@ -153,6 +155,7 @@ constructor(
),
transition,
) {
+ logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
Trace.endSection()
@@ -205,4 +208,23 @@ constructor(
apply()
}
}
+
+ private fun logAlphaVisibilityOfAppliedConstraintSet(
+ cs: ConstraintSet,
+ viewModel: KeyguardClockViewModel
+ ) {
+ if (!DEBUG || viewModel.clock == null) return
+ val smallClockViewId = R.id.lockscreen_clock_view
+ val largeClockViewId = viewModel.clock!!.largeClock.layout.views[0].id
+ Log.i(
+ TAG,
+ "applyCsToSmallClock: vis=${cs.getVisibility(smallClockViewId)} " +
+ "alpha=${cs.getConstraint(smallClockViewId).propertySet}"
+ )
+ Log.i(
+ TAG,
+ "applyCsToLargeClock: vis=${cs.getVisibility(largeClockViewId)} " +
+ "alpha=${cs.getConstraint(largeClockViewId).propertySet}"
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index b56c99864c0b..46c354a45c92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -72,38 +72,47 @@ object KeyguardPreviewClockViewBinder {
@JvmStatic
fun bind(
context: Context,
+ displayId: Int,
rootView: ConstraintLayout,
viewModel: KeyguardPreviewClockViewModel,
clockEventController: ClockEventController,
- updateClockAppearance: KSuspendFunction1<ClockController, Unit>
+ updateClockAppearance: KSuspendFunction1<ClockController, Unit>,
) {
+ // TODO(b/327668072): When this function is called multiple times, the clock view can be
+ // gone due to a race condition on removeView and addView.
rootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- combine(viewModel.selectedClockSize, viewModel.previewClock) { _, clock ->
+ combine(viewModel.selectedClockSize, viewModel.previewClockPair) { _, clock ->
clock
}
- .collect { previewClock ->
- viewModel.lastClock?.let { lastClock ->
- (lastClock.largeClock.layout.views +
- lastClock.smallClock.layout.views)
+ .collect { previewClockPair ->
+ viewModel.lastClockPair?.let { clockPair ->
+ (clockPair.first.largeClock.layout.views +
+ clockPair.first.smallClock.layout.views)
+ .forEach { rootView.removeView(it) }
+ (clockPair.second.largeClock.layout.views +
+ clockPair.second.smallClock.layout.views)
.forEach { rootView.removeView(it) }
}
- viewModel.lastClock = previewClock
- clockEventController.clock = previewClock
- updateClockAppearance(previewClock)
+ viewModel.lastClockPair = previewClockPair
+ val clockPreview =
+ if (displayId == 0) previewClockPair.first
+ else previewClockPair.second
+ clockEventController.clock = clockPreview
+ updateClockAppearance(clockPreview)
if (viewModel.shouldHighlightSelectedAffordance) {
- (previewClock.largeClock.layout.views +
- previewClock.smallClock.layout.views)
+ (clockPreview.largeClock.layout.views +
+ clockPreview.smallClock.layout.views)
.forEach { it.alpha = KeyguardPreviewRenderer.DIM_ALPHA }
}
- previewClock.largeClock.layout.views.forEach {
+ clockPreview.largeClock.layout.views.forEach {
(it.parent as? ViewGroup)?.removeView(it)
rootView.addView(it)
}
- previewClock.smallClock.layout.views.forEach {
+ clockPreview.smallClock.layout.views.forEach {
(it.parent as? ViewGroup)?.removeView(it)
rootView.addView(it)
}
@@ -164,10 +173,12 @@ object KeyguardPreviewClockViewBinder {
viewModel: KeyguardPreviewClockViewModel
) {
val cs = ConstraintSet().apply { clone(rootView) }
- val clock = viewModel.previewClock.value
+ val clockPair = viewModel.previewClockPair.value
applyClockDefaultConstraints(context, cs)
- clock.largeClock.layout.applyPreviewConstraints(cs)
- clock.smallClock.layout.applyPreviewConstraints(cs)
+ clockPair.first.largeClock.layout.applyPreviewConstraints(cs)
+ clockPair.first.smallClock.layout.applyPreviewConstraints(cs)
+ clockPair.second.largeClock.layout.applyPreviewConstraints(cs)
+ clockPair.second.smallClock.layout.applyPreviewConstraints(cs)
// When selectedClockSize is the initial value, make both clocks invisible to avoid
// flickering
@@ -185,8 +196,10 @@ object KeyguardPreviewClockViewBinder {
}
cs.apply {
- setVisibility(clock.largeClock.layout.views, largeClockVisibility)
- setVisibility(clock.smallClock.layout.views, smallClockVisibility)
+ setVisibility(clockPair.first.largeClock.layout.views, largeClockVisibility)
+ setVisibility(clockPair.first.smallClock.layout.views, smallClockVisibility)
+ setVisibility(clockPair.second.largeClock.layout.views, largeClockVisibility)
+ setVisibility(clockPair.second.smallClock.layout.views, smallClockVisibility)
}
cs.applyTo(rootView)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index f95efaab75f4..7c76e6afc074 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -408,6 +408,7 @@ constructor(
if (migrateClocksToBlueprint()) {
KeyguardPreviewClockViewBinder.bind(
context,
+ displayId,
keyguardRootView,
clockViewModel,
clockController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index a22671d42e94..a183b720c087 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -52,6 +52,11 @@ internal fun ConstraintSet.setVisibility(
visibility: Int,
) = views.forEach { view -> this.setVisibility(view.id, visibility) }
+internal fun ConstraintSet.setAlpha(
+ views: Iterable<View>,
+ alpha: Float,
+) = views.forEach { view -> this.setAlpha(view.id, alpha) }
+
open class ClockSection
@Inject
constructor(
@@ -101,6 +106,8 @@ constructor(
return constraintSet.apply {
setVisibility(getTargetClockFace(clock).views, VISIBLE)
setVisibility(getNonTargetClockFace(clock).views, GONE)
+ setAlpha(getTargetClockFace(clock).views, 1F)
+ setAlpha(getNonTargetClockFace(clock).views, 0F)
if (!keyguardClockViewModel.useLargeClock) {
connect(sharedR.id.bc_smartspace_view, TOP, sharedR.id.date_smartspace_view, BOTTOM)
}
@@ -179,7 +186,7 @@ constructor(
context.resources.getDimensionPixelSize(customizationR.dimen.clock_padding_start) +
context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
)
- var smallClockTopMargin =
+ val smallClockTopMargin =
if (splitShadeStateController.shouldUseSplitNotificationShade(context.resources)) {
context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index f65f3760f082..6184c82cbff7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -199,8 +199,8 @@ class ClockSizeTransition(
private val TRANSITION_PROPERTIES =
arrayOf(PROP_VISIBILITY, PROP_ALPHA, PROP_BOUNDS, SMARTSPACE_BOUNDS)
- private val DEBUG = true
- private val TAG = ClockFaceInTransition::class.simpleName!!
+ private val DEBUG = false
+ private val TAG = VisibilityBoundsTransition::class.simpleName!!
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index b92a9a08987a..7e39a884a69e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -16,13 +16,16 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
/** Breaks down AOD->GONE transition into discrete steps for corresponding views to consume. */
@ExperimentalCoroutinesApi
@@ -40,5 +43,19 @@ constructor(
to = KeyguardState.GONE,
)
+ /**
+ * AOD -> GONE should fade out the lockscreen contents. This transition plays both during wake
+ * and unlock, and also during insecure camera launch (which is GONE -> AOD (canceled) -> GONE).
+ */
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var startAlpha = 1f
+ return transitionAnimation.sharedFlow(
+ duration = 200.milliseconds,
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 0f, it) },
+ onFinish = { 0f },
+ )
+ }
+
override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index 105a7ed52311..445575f7e55d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -16,12 +16,15 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
/** Breaks down AOD->OCCLUDED transition into discrete steps for corresponding views to consume. */
@SysUISingleton
@@ -37,5 +40,23 @@ constructor(
to = KeyguardState.OCCLUDED,
)
+ /**
+ * Fade out the lockscreen during a transition to OCCLUDED.
+ *
+ * This happens when pressing the power button while a SHOW_WHEN_LOCKED activity is on the top
+ * of the task stack, as well as when the power button is double tapped on the LOCKSCREEN (the
+ * first tap transitions to AOD, the second cancels that transition and starts AOD -> OCCLUDED.
+ */
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var currentAlpha = 0f
+ return transitionAnimation.sharedFlow(
+ duration = 250.milliseconds,
+ startTime = 100.milliseconds, // Wait for the light reveal to "hit" the LS elements.
+ onStart = { currentAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
+ onCancel = { 0f },
+ )
+ }
+
override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 31e809356e02..bd19c8006e23 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -180,7 +180,7 @@ constructor(
}
private val isUnlocked: Flow<Boolean> =
- deviceEntryInteractor.isUnlocked.flatMapLatest { isUnlocked ->
+ keyguardInteractor.isKeyguardDismissible.flatMapLatest { isUnlocked ->
if (!isUnlocked) {
flowOf(false)
} else {
@@ -197,7 +197,7 @@ constructor(
val iconType: Flow<DeviceEntryIconView.IconType> =
combine(
deviceEntryUdfpsInteractor.isListeningForUdfps,
- keyguardInteractor.isKeyguardDismissible,
+ isUnlocked,
) { isListeningForUdfps, isUnlocked ->
if (isListeningForUdfps) {
DeviceEntryIconView.IconType.FINGERPRINT
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index fca1604946e1..8851a51f15b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -16,12 +16,14 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -41,6 +43,16 @@ constructor(
to = KeyguardState.GONE,
)
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var startAlpha = 1f
+ return transitionAnimation.sharedFlow(
+ duration = 200.milliseconds,
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 0f, it) },
+ onFinish = { 0f },
+ )
+ }
+
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
new file mode 100644
index 000000000000..c0b11959cbd9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.util.MathUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down DOZING->OCCLUDED transition into discrete steps for corresponding views to consume.
+ */
+@SysUISingleton
+class DozingToOccludedTransitionViewModel
+@Inject
+constructor(
+ animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
+ from = KeyguardState.DOZING,
+ to = KeyguardState.OCCLUDED,
+ )
+
+ /**
+ * Fade out the lockscreen during a transition to OCCLUDED.
+ *
+ * This happens when pressing the power button while a SHOW_WHEN_LOCKED activity is on the top
+ * of the task stack, as well as when the power button is double tapped on the LOCKSCREEN (the
+ * first tap transitions to DOZING, the second cancels that transition and starts DOZING ->
+ * OCCLUDED.
+ */
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var currentAlpha = 0f
+ return transitionAnimation.sharedFlow(
+ duration = 250.milliseconds,
+ startTime = 100.milliseconds, // Wait for the light reveal to "hit" the LS elements.
+ onStart = { currentAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
+ onCancel = { 0f },
+ )
+ }
+
+ override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
index f95a8a727644..b9ff25926f02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
@@ -45,9 +45,10 @@ constructor(
val isSmallClockVisible: Flow<Boolean> =
interactor.selectedClockSize.map { it == SettingsClockSize.SMALL }
- var lastClock: ClockController? = null
+ var lastClockPair: Pair<ClockController, ClockController>? = null
- val previewClock: StateFlow<ClockController> = interactor.previewClock
+ val previewClockPair: StateFlow<Pair<ClockController, ClockController>> =
+ interactor.previewClockPair
val selectedClockSize: StateFlow<SettingsClockSize?> =
interactor.selectedClockSize.stateIn(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index bdcaf0951c5b..f848717c2170 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
@@ -70,8 +71,13 @@ constructor(
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
+ private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
+ private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
+ private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel,
+ private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
@@ -120,6 +126,27 @@ constructor(
}
.distinctUntilChanged()
+ /**
+ * Keyguard should not show while the communal hub is fully visible. This check is added since
+ * at the moment, closing the notification shade will cause the keyguard alpha to be set back to
+ * 1. Also ensure keyguard is never visible when GONE.
+ */
+ private val hideKeyguard: Flow<Boolean> =
+ combine(
+ communalInteractor.isIdleOnCommunal,
+ keyguardTransitionInteractor
+ .transitionValue(GONE)
+ .map { it == 1f }
+ .onStart { emit(false) },
+ keyguardTransitionInteractor
+ .transitionValue(OCCLUDED)
+ .map { it == 1f }
+ .onStart { emit(false) },
+ ) { isIdleOnCommunal, isGone, isOccluded ->
+ isIdleOnCommunal || isGone || isOccluded
+ }
+ .distinctUntilChanged()
+
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
@@ -136,20 +163,20 @@ constructor(
/** An observable for the alpha level for the entire keyguard root view. */
fun alpha(viewState: ViewStateAccessor): Flow<Float> {
return combine(
- communalInteractor.isIdleOnCommunal,
- keyguardTransitionInteractor
- .transitionValue(GONE)
- .map { it == 1f }
- .onStart { emit(false) }
- .distinctUntilChanged(),
+ hideKeyguard,
// The transitions are mutually exclusive, so they are safe to merge to get the last
// value emitted by any of them. Do not add flows that cannot make this guarantee.
merge(
alphaOnShadeExpansion,
keyguardInteractor.dismissAlpha.filterNotNull(),
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
+ aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
+ aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dozingToGoneTransitionViewModel.lockscreenAlpha(viewState),
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
+ dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
goneToAodTransitionViewModel.enterFromTopAnimationAlpha,
goneToDozingTransitionViewModel.lockscreenAlpha,
@@ -167,12 +194,8 @@ constructor(
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
)
.onStart { emit(1f) }
- ) { isIdleOnCommunal, gone, alpha ->
- if (isIdleOnCommunal || gone) {
- // Keyguard should not show while the communal hub is fully visible. This check
- // is added since at the moment, closing the notification shade will cause the
- // keyguard alpha to be set back to 1. Also ensure keyguard is never visible
- // when GONE.
+ ) { hideKeyguard, alpha ->
+ if (hideKeyguard) {
0f
} else {
alpha
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index d79288947e78..23320be129fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -39,6 +40,7 @@ constructor(
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
val longPress: KeyguardLongPressViewModel,
+ val splitShadeStateController: SplitShadeStateController,
) {
private val clockSize = clockInteractor.clockSize
@@ -46,8 +48,10 @@ constructor(
get() = authController.isUdfpsSupported
val isLargeClockVisible: Boolean
get() = clockSize.value == KeyguardClockSwitch.LARGE
- val areNotificationsVisible: Boolean
- get() = !isLargeClockVisible
+ fun areNotificationsVisible(resources: Resources): Boolean {
+ return !isLargeClockVisible ||
+ splitShadeStateController.shouldUseSplitNotificationShade(resources)
+ }
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (isLargeClockVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 9afe8fcd93d0..b60e99973348 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,11 +16,12 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -51,13 +52,13 @@ constructor(
)
private fun upDestinationSceneKey(isUnlocked: Boolean): SceneKey {
- return if (isUnlocked) SceneKey.Gone else SceneKey.Bouncer
+ return if (isUnlocked) Scenes.Gone else Scenes.Bouncer
}
/** The key of the scene we should switch to when swiping left. */
val leftDestinationSceneKey: StateFlow<SceneKey?> =
communalInteractor.isCommunalAvailable
- .map { available -> if (available) SceneKey.Communal else null }
+ .map { available -> if (available) Scenes.Communal else null }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
new file mode 100644
index 000000000000..027a739b4abf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import javax.inject.Inject
+
+class MediaCarouselViewModel @Inject constructor(mediaDataManager: MediaDataManager) {
+ val isMediaVisible: Boolean = mediaDataManager.hasActiveMediaOrRecommendation()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 8b7c85b65824..f2013bec19c7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -546,7 +546,7 @@ public class LogModule {
@SysUISingleton
@KeyguardLog
public static LogBuffer provideKeyguardLogBuffer(LogBufferFactory factory) {
- return factory.create("KeyguardLog", 250);
+ return factory.create("KeyguardLog", 500);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt
new file mode 100644
index 000000000000..2850b4bb2358
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.util
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the media_controls_refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object MediaControlsRefactorFlag {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_MEDIA_CONTROLS_REFACTOR
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the flag enabled? */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.mediaControlsRefactor()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 15747b9eb515..d4bd6daedfab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -58,4 +58,7 @@ constructor(
/** Check whether to use scene framework */
fun isSceneContainerEnabled() =
sceneContainerFlags.isEnabled() && MediaInSceneContainerFlag.isEnabled
+
+ /** Check whether to use media refactor code */
+ fun isMediaControlsRefactorEnabled() = MediaControlsRefactorFlag.isEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
index f1cade7512e2..0b19bab5c7c5 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
@@ -24,12 +24,14 @@ import android.media.projection.ReviewGrantedConsentResult
import android.os.RemoteException
import android.os.ServiceManager
import android.util.Log
+import android.window.WindowContainerToken
+import javax.inject.Inject
/**
* Helper class that handles the media projection service related actions. It simplifies invoking
* the MediaProjectionManagerService and updating the permission consent.
*/
-class MediaProjectionServiceHelper {
+class MediaProjectionServiceHelper @Inject constructor() {
companion object {
private const val TAG = "MediaProjectionServiceHelper"
private val service =
@@ -90,4 +92,16 @@ class MediaProjectionServiceHelper {
}
}
}
+
+ /** Updates the projected task to the task that has a matching [WindowContainerToken]. */
+ fun updateTaskRecordingSession(token: WindowContainerToken): Boolean {
+ return try {
+ true
+ // TODO: actually call the service once it is implemented
+ // service.updateTaskRecordingSession(token)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Unable to updateTaskRecordingSession", e)
+ false
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index e1741c73d453..7c7efd0be8ed 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -26,12 +26,13 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.res.R
+import com.android.systemui.Flags.pssAppSelectorAbruptExitFix
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
+import com.android.systemui.res.R
import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration
import javax.inject.Inject
@@ -122,14 +123,7 @@ constructor(
override fun onRecentAppClicked(task: RecentTask, view: View) {
val launchCookie = LaunchCookie()
- val activityOptions =
- ActivityOptions.makeScaleUpAnimation(
- view,
- /* startX= */ 0,
- /* startY= */ 0,
- view.width,
- view.height
- )
+ val activityOptions = createAnimation(task, view)
activityOptions.pendingIntentBackgroundActivityStartMode =
MODE_BACKGROUND_ACTIVITY_START_ALLOWED
activityOptions.setLaunchCookie(launchCookie)
@@ -139,6 +133,28 @@ constructor(
resultHandler.returnSelectedApp(launchCookie)
}
+ private fun createAnimation(task: RecentTask, view: View): ActivityOptions =
+ if (pssAppSelectorAbruptExitFix() && task.isForegroundTask) {
+ // When the selected task is in the foreground, the scale up animation doesn't work.
+ // We fallback to the default close animation.
+ ActivityOptions.makeCustomTaskAnimation(
+ view.context,
+ /* enterResId= */ 0,
+ /* exitResId= */ com.android.internal.R.anim.resolver_close_anim,
+ /* handler = */ null,
+ /* startedListener = */ null,
+ /* finishedListener = */ null
+ )
+ } else {
+ ActivityOptions.makeScaleUpAnimation(
+ view,
+ /* startX= */ 0,
+ /* startY= */ 0,
+ view.width,
+ view.height
+ )
+ }
+
override fun onTaskSizeChanged(size: Rect) {
views?.recentsContainer?.setTaskHeightSize()
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt
deleted file mode 100644
index fc452288f86d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.mediaprojection.devicepolicy
-
-import android.content.Context
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.phone.SystemUIDialog
-
-/** Dialog that shows that screen capture is disabled on this device. */
-class ScreenCaptureDisabledDialog(context: Context) : SystemUIDialog(context) {
-
- init {
- setTitle(context.getString(R.string.screen_capturing_disabled_by_policy_dialog_title))
- setMessage(
- context.getString(R.string.screen_capturing_disabled_by_policy_dialog_description)
- )
- setIcon(R.drawable.ic_cast)
- setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> cancel() }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialogDelegate.kt
new file mode 100644
index 000000000000..8aed535956b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialogDelegate.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.mediaprojection.devicepolicy
+
+import android.content.DialogInterface.BUTTON_POSITIVE
+import android.content.res.Resources
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+
+/** Dialog that shows that screen capture is disabled on this device. */
+class ScreenCaptureDisabledDialogDelegate @Inject constructor(
+ @Main private val resources: Resources,
+ private val systemUIDialogFactory: SystemUIDialog.Factory
+) : SystemUIDialog.Delegate {
+
+ override fun createDialog(): SystemUIDialog {
+ val dialog = systemUIDialogFactory.create(this)
+ dialog.setTitle(resources.getString(R.string.screen_capturing_disabled_by_policy_dialog_title))
+ dialog.setMessage(
+ resources.getString(R.string.screen_capturing_disabled_by_policy_dialog_description)
+ )
+ dialog.setIcon(R.drawable.ic_cast)
+ dialog.setButton(BUTTON_POSITIVE, resources.getString(android.R.string.ok)) {
+ _, _ -> dialog.cancel()
+ }
+
+ return dialog
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 8b034b293dcb..17f9cafcb650 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -59,7 +59,7 @@ import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
-import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.AlertDialogWithDelegate;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -79,6 +79,7 @@ public class MediaProjectionPermissionActivity extends Activity
private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
private final StatusBarManager mStatusBarManager;
private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
+ private final ScreenCaptureDisabledDialogDelegate mScreenCaptureDisabledDialogDelegate;
private String mPackageName;
private int mUid;
@@ -93,14 +94,17 @@ public class MediaProjectionPermissionActivity extends Activity
private boolean mUserSelectingTask = false;
@Inject
- public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
+ public MediaProjectionPermissionActivity(
+ FeatureFlags featureFlags,
Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
StatusBarManager statusBarManager,
- MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
+ ScreenCaptureDisabledDialogDelegate screenCaptureDisabledDialogDelegate) {
mFeatureFlags = featureFlags;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
mStatusBarManager = statusBarManager;
mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
+ mScreenCaptureDisabledDialogDelegate = screenCaptureDisabledDialogDelegate;
}
@Override
@@ -315,10 +319,7 @@ public class MediaProjectionPermissionActivity extends Activity
final UserHandle hostUserHandle = getHostUserHandle();
if (mScreenCaptureDevicePolicyResolver.get()
.isScreenCaptureCompletelyDisabled(hostUserHandle)) {
- // Using application context for the dialog, instead of the activity context, so we get
- // the correct screen width when in split screen.
- Context dialogContext = getApplicationContext();
- AlertDialog dialog = new ScreenCaptureDisabledDialog(dialogContext);
+ AlertDialog dialog = mScreenCaptureDisabledDialogDelegate.createDialog();
setUpDialog(dialog);
dialog.show();
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
index 9938f11e5d4c..cfbcaf91b791 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
@@ -16,11 +16,11 @@
package com.android.systemui.mediaprojection.taskswitcher.data.model
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
/** Represents the state of media projection. */
sealed interface MediaProjectionState {
object NotProjecting : MediaProjectionState
object EntireScreen : MediaProjectionState
- data class SingleTask(val task: TaskInfo) : MediaProjectionState
+ data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
index 492d482459d6..4ff54d4eae65 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
@@ -17,10 +17,14 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityOptions
+import android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.app.ActivityTaskManager
+import android.app.IActivityTaskManager
import android.app.TaskStackListener
import android.os.IBinder
import android.util.Log
+import android.view.Display
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -40,11 +44,24 @@ import kotlinx.coroutines.withContext
class ActivityTaskManagerTasksRepository
@Inject
constructor(
- private val activityTaskManager: ActivityTaskManager,
+ private val activityTaskManager: IActivityTaskManager,
@Application private val applicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : TasksRepository {
+ override suspend fun launchRecentTask(taskInfo: RunningTaskInfo) {
+ withContext(backgroundDispatcher) {
+ val activityOptions = ActivityOptions.makeBasic()
+ activityOptions.pendingIntentBackgroundActivityStartMode =
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ activityOptions.launchDisplayId = taskInfo.displayId
+ activityTaskManager.startActivityFromRecents(
+ taskInfo.taskId,
+ activityOptions.toBundle()
+ )
+ }
+ }
+
override suspend fun findRunningTaskFromWindowContainerToken(
windowContainerToken: IBinder
): RunningTaskInfo? =
@@ -53,7 +70,14 @@ constructor(
}
private suspend fun getRunningTasks(): List<RunningTaskInfo> =
- withContext(backgroundDispatcher) { activityTaskManager.getTasks(Integer.MAX_VALUE) }
+ withContext(backgroundDispatcher) {
+ activityTaskManager.getTasks(
+ /* maxNum = */ Integer.MAX_VALUE,
+ /* filterForVisibleRecents = */ false,
+ /* keepIntentExtra = */ false,
+ /* displayId = */ Display.INVALID_DISPLAY
+ )
+ }
override val foregroundTask: Flow<RunningTaskInfo> =
conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
index 6480a47e8ea2..74d19921c706 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
+import android.app.ActivityManager.RunningTaskInfo
import android.media.projection.MediaProjectionInfo
import android.media.projection.MediaProjectionManager
import android.os.Handler
@@ -26,15 +27,19 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
@SysUISingleton
class MediaProjectionManagerRepository
@@ -43,9 +48,21 @@ constructor(
private val mediaProjectionManager: MediaProjectionManager,
@Main private val handler: Handler,
@Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
private val tasksRepository: TasksRepository,
+ private val mediaProjectionServiceHelper: MediaProjectionServiceHelper,
) : MediaProjectionRepository {
+ override suspend fun switchProjectedTask(task: RunningTaskInfo) {
+ withContext(backgroundDispatcher) {
+ if (mediaProjectionServiceHelper.updateTaskRecordingSession(task.token)) {
+ Log.d(TAG, "Successfully switched projected task")
+ } else {
+ Log.d(TAG, "Failed to switch projected task")
+ }
+ }
+ }
+
override val mediaProjectionState: Flow<MediaProjectionState> =
conflatedCallbackFlow {
val callback =
@@ -82,7 +99,9 @@ constructor(
}
val matchingTask =
tasksRepository.findRunningTaskFromWindowContainerToken(
- checkNotNull(session.tokenToRecord)) ?: return MediaProjectionState.EntireScreen
+ checkNotNull(session.tokenToRecord)
+ )
+ ?: return MediaProjectionState.EntireScreen
return MediaProjectionState.SingleTask(matchingTask)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
index 5bec6925babe..e495466008ce 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
@@ -16,12 +16,16 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
+import android.app.ActivityManager.RunningTaskInfo
import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
import kotlinx.coroutines.flow.Flow
/** Represents a repository to retrieve and change data related to media projection. */
interface MediaProjectionRepository {
+ /** Switches the task that should be projected. */
+ suspend fun switchProjectedTask(task: RunningTaskInfo)
+
/** Represents the current [MediaProjectionState]. */
val mediaProjectionState: Flow<MediaProjectionState>
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt
deleted file mode 100644
index 544eb6b99d4f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.mediaprojection.taskswitcher.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-
-/**
- * No-op implementation of [MediaProjectionRepository] that does nothing. Currently used as a
- * placeholder, while the real implementation is not completed.
- */
-@SysUISingleton
-class NoOpMediaProjectionRepository @Inject constructor() : MediaProjectionRepository {
-
- override val mediaProjectionState: Flow<MediaProjectionState> = emptyFlow()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt
index 6a535e4ecc50..9ef42b4de45c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt
@@ -23,6 +23,8 @@ import kotlinx.coroutines.flow.Flow
/** Repository responsible for retrieving data related to running tasks. */
interface TasksRepository {
+ suspend fun launchRecentTask(taskInfo: RunningTaskInfo)
+
/**
* Tries to find a [RunningTaskInfo] with a matching window container token. Returns `null` when
* no matching task was found.
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
index fc5cf7d75bdf..eb9e6a5de057 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.domain.interactor
+import android.app.ActivityManager.RunningTaskInfo
import android.app.TaskInfo
import android.content.Intent
import android.util.Log
@@ -37,10 +38,18 @@ import kotlinx.coroutines.flow.map
class TaskSwitchInteractor
@Inject
constructor(
- mediaProjectionRepository: MediaProjectionRepository,
+ private val mediaProjectionRepository: MediaProjectionRepository,
private val tasksRepository: TasksRepository,
) {
+ suspend fun switchProjectedTask(task: RunningTaskInfo) {
+ mediaProjectionRepository.switchProjectedTask(task)
+ }
+
+ suspend fun goBackToTask(task: RunningTaskInfo) {
+ tasksRepository.launchRecentTask(task)
+ }
+
/**
* Emits a stream of changes to the state of task switching, in the context of media projection.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt
index cd1258ed6aa8..caabc64efae1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.domain.model
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
/** Represents tha state of task switching in the context of single task media projection. */
sealed interface TaskSwitchState {
@@ -25,6 +25,8 @@ sealed interface TaskSwitchState {
/** The foreground task is the same as the task that is currently being projected. */
object TaskUnchanged : TaskSwitchState
/** The foreground task is a different one to the task it currently being projected. */
- data class TaskSwitched(val projectedTask: TaskInfo, val foregroundTask: TaskInfo) :
- TaskSwitchState
+ data class TaskSwitched(
+ val projectedTask: RunningTaskInfo,
+ val foregroundTask: RunningTaskInfo
+ ) : TaskSwitchState
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
index 7840da960a83..dab7439f0f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
@@ -16,23 +16,25 @@
package com.android.systemui.mediaprojection.taskswitcher.ui
+import android.app.ActivityManager.RunningTaskInfo
import android.app.Notification
-import android.app.NotificationChannel
import android.app.NotificationManager
+import android.app.PendingIntent
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Parcelable
import android.util.Log
-import com.android.systemui.res.R
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.NotShowing
import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.Showing
import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel
+import com.android.systemui.res.R
import com.android.systemui.util.NotificationChannels
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
/** Coordinator responsible for showing/hiding the task switcher notification. */
@@ -43,32 +45,54 @@ constructor(
private val context: Context,
private val notificationManager: NotificationManager,
@Application private val applicationScope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
private val viewModel: TaskSwitcherNotificationViewModel,
+ private val broadcastDispatcher: BroadcastDispatcher,
) {
+
fun start() {
applicationScope.launch {
- viewModel.uiState.flowOn(mainDispatcher).collect { uiState ->
- Log.d(TAG, "uiState -> $uiState")
- when (uiState) {
- is Showing -> showNotification()
- is NotShowing -> hideNotification()
+ launch {
+ viewModel.uiState.collect { uiState ->
+ Log.d(TAG, "uiState -> $uiState")
+ when (uiState) {
+ is Showing -> showNotification(uiState)
+ is NotShowing -> hideNotification()
+ }
}
}
+ launch {
+ broadcastDispatcher
+ .broadcastFlow(IntentFilter(SWITCH_ACTION)) { intent, _ ->
+ intent.requireParcelableExtra<RunningTaskInfo>(EXTRA_ACTION_TASK)
+ }
+ .collect { task: RunningTaskInfo ->
+ Log.d(TAG, "Switch action triggered: $task")
+ viewModel.onSwitchTaskClicked(task)
+ }
+ }
+ launch {
+ broadcastDispatcher
+ .broadcastFlow(IntentFilter(GO_BACK_ACTION)) { intent, _ ->
+ intent.requireParcelableExtra<RunningTaskInfo>(EXTRA_ACTION_TASK)
+ }
+ .collect { task ->
+ Log.d(TAG, "Go back action triggered: $task")
+ viewModel.onGoBackToTaskClicked(task)
+ }
+ }
}
}
- private fun showNotification() {
- notificationManager.notify(TAG, NOTIFICATION_ID, createNotification())
+ private fun showNotification(uiState: Showing) {
+ notificationManager.notify(TAG, NOTIFICATION_ID, createNotification(uiState))
}
- private fun createNotification(): Notification {
- // TODO(b/286201261): implement actions
+ private fun createNotification(uiState: Showing): Notification {
val actionSwitch =
Notification.Action.Builder(
/* icon = */ null,
context.getString(R.string.media_projection_task_switcher_action_switch),
- /* intent = */ null
+ createActionPendingIntent(action = SWITCH_ACTION, task = uiState.foregroundTask)
)
.build()
@@ -76,34 +100,40 @@ constructor(
Notification.Action.Builder(
/* icon = */ null,
context.getString(R.string.media_projection_task_switcher_action_back),
- /* intent = */ null
+ createActionPendingIntent(action = GO_BACK_ACTION, task = uiState.projectedTask)
)
.build()
-
- val channel =
- NotificationChannel(
- NotificationChannels.HINTS,
- context.getString(R.string.media_projection_task_switcher_notification_channel),
- NotificationManager.IMPORTANCE_HIGH
- )
- notificationManager.createNotificationChannel(channel)
- return Notification.Builder(context, channel.id)
+ return Notification.Builder(context, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.qs_screen_record_icon_on)
.setAutoCancel(true)
.setContentText(context.getString(R.string.media_projection_task_switcher_text))
.addAction(actionSwitch)
.addAction(actionBack)
- .setPriority(Notification.PRIORITY_HIGH)
- .setDefaults(Notification.DEFAULT_VIBRATE)
.build()
}
private fun hideNotification() {
- notificationManager.cancel(NOTIFICATION_ID)
+ notificationManager.cancel(TAG, NOTIFICATION_ID)
}
+ private fun createActionPendingIntent(action: String, task: RunningTaskInfo) =
+ PendingIntent.getBroadcast(
+ context,
+ /* requestCode= */ 0,
+ Intent(action).apply { putExtra(EXTRA_ACTION_TASK, task) },
+ /* flags= */ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+
companion object {
private const val TAG = "TaskSwitchNotifCoord"
private const val NOTIFICATION_ID = 5566
+
+ private const val EXTRA_ACTION_TASK = "extra_task"
+
+ private const val SWITCH_ACTION = "com.android.systemui.mediaprojection.SWITCH_TASK"
+ private const val GO_BACK_ACTION = "com.android.systemui.mediaprojection.GO_BACK"
}
}
+
+private fun <T : Parcelable> Intent.requireParcelableExtra(key: String) =
+ getParcelableExtra<T>(key)!!
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt
index 21aee72d17ae..f307761a1875 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.ui.model
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
/** Represents the UI state for the task switcher notification. */
sealed interface TaskSwitcherNotificationUiState {
@@ -24,7 +24,7 @@ sealed interface TaskSwitcherNotificationUiState {
object NotShowing : TaskSwitcherNotificationUiState
/** The notification should be shown. */
data class Showing(
- val projectedTask: TaskInfo,
- val foregroundTask: TaskInfo,
+ val projectedTask: RunningTaskInfo,
+ val foregroundTask: RunningTaskInfo,
) : TaskSwitcherNotificationUiState
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt
index d9754d4429d4..d6629e07d87a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt
@@ -16,34 +16,64 @@
package com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel
+import android.app.ActivityManager.RunningTaskInfo
import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
import com.android.systemui.mediaprojection.taskswitcher.domain.model.TaskSwitchState
import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.transformLatest
+import kotlinx.coroutines.withContext
-class TaskSwitcherNotificationViewModel @Inject constructor(interactor: TaskSwitchInteractor) {
+class TaskSwitcherNotificationViewModel
+@Inject
+constructor(
+ private val interactor: TaskSwitchInteractor,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
val uiState: Flow<TaskSwitcherNotificationUiState> =
- interactor.taskSwitchChanges.map { taskSwitchChange ->
- Log.d(TAG, "taskSwitchChange: $taskSwitchChange")
- when (taskSwitchChange) {
- is TaskSwitchState.TaskSwitched -> {
- TaskSwitcherNotificationUiState.Showing(
- projectedTask = taskSwitchChange.projectedTask,
- foregroundTask = taskSwitchChange.foregroundTask,
- )
+ interactor.taskSwitchChanges
+ .map { taskSwitchChange ->
+ Log.d(TAG, "taskSwitchChange: $taskSwitchChange")
+ when (taskSwitchChange) {
+ is TaskSwitchState.TaskSwitched -> {
+ TaskSwitcherNotificationUiState.Showing(
+ projectedTask = taskSwitchChange.projectedTask,
+ foregroundTask = taskSwitchChange.foregroundTask,
+ )
+ }
+ is TaskSwitchState.NotProjectingTask,
+ is TaskSwitchState.TaskUnchanged -> {
+ TaskSwitcherNotificationUiState.NotShowing
+ }
}
- is TaskSwitchState.NotProjectingTask,
- is TaskSwitchState.TaskUnchanged -> {
- TaskSwitcherNotificationUiState.NotShowing
+ }
+ .transformLatest { uiState ->
+ emit(uiState)
+ if (uiState is TaskSwitcherNotificationUiState.Showing) {
+ delay(NOTIFICATION_MAX_SHOW_DURATION)
+ Log.d(TAG, "Auto hiding notification after $NOTIFICATION_MAX_SHOW_DURATION")
+ emit(TaskSwitcherNotificationUiState.NotShowing)
}
}
- }
+
+ suspend fun onSwitchTaskClicked(task: RunningTaskInfo) {
+ interactor.switchProjectedTask(task)
+ }
+
+ suspend fun onGoBackToTaskClicked(task: RunningTaskInfo) =
+ withContext(backgroundDispatcher) { interactor.goBackToTask(task) }
companion object {
+ @VisibleForTesting val NOTIFICATION_MAX_SHOW_DURATION = 5.seconds
private const val TAG = "TaskSwitchNotifVM"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index 6eb62263eb9a..e7b6e6373f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -16,11 +16,12 @@
package com.android.systemui.model
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
@@ -67,11 +68,11 @@ constructor(
*/
val EvaluatorByFlag =
mapOf<Int, (SceneKey) -> Boolean>(
- SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it != SceneKey.Gone },
- SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it == SceneKey.Shade },
- SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it == SceneKey.QuickSettings },
- SYSUI_STATE_BOUNCER_SHOWING to { it == SceneKey.Bouncer },
- SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it == SceneKey.Lockscreen },
+ SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it != Scenes.Gone },
+ SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it == Scenes.Shade },
+ SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it == Scenes.QuickSettings },
+ SYSUI_STATE_BOUNCER_SHOWING to { it == Scenes.Bouncer },
+ SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it == Scenes.Lockscreen },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
index c74a71c52260..5c4915689f22 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
@@ -25,11 +25,11 @@ import com.android.systemui.dagger.qualifiers.DisplayId
* ```
* sysuiState.updateFlags(
* displayId,
- * SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to (sceneKey != SceneKey.Gone),
- * SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to (sceneKey == SceneKey.Shade),
- * SYSUI_STATE_QUICK_SETTINGS_EXPANDED to (sceneKey == SceneKey.QuickSettings),
- * SYSUI_STATE_BOUNCER_SHOWING to (sceneKey == SceneKey.Bouncer),
- * SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to (sceneKey == SceneKey.Lockscreen),
+ * SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to (sceneKey != Scenes.Gone),
+ * SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to (sceneKey == Scenes.Shade),
+ * SYSUI_STATE_QUICK_SETTINGS_EXPANDED to (sceneKey == Scenes.QuickSettings),
+ * SYSUI_STATE_BOUNCER_SHOWING to (sceneKey == Scenes.Bouncer),
+ * SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to (sceneKey == Scenes.Lockscreen),
* )
* ```
*
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 152f193be3f9..9f7d1b34151a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -19,6 +19,7 @@ package com.android.systemui.navigationbar;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
@@ -28,7 +29,6 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
-import android.os.Handler;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -70,9 +70,11 @@ import dalvik.annotation.optimization.NeverCompile;
import java.io.PrintWriter;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
+
@SysUISingleton
public class NavigationBarControllerImpl implements
ConfigurationController.ConfigurationListener,
@@ -82,7 +84,7 @@ public class NavigationBarControllerImpl implements
private static final String TAG = NavigationBarControllerImpl.class.getSimpleName();
private final Context mContext;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
private final SecureSettings mSecureSettings;
private final DisplayTracker mDisplayTracker;
@@ -119,7 +121,7 @@ public class NavigationBarControllerImpl implements
NavigationModeController navigationModeController,
SysUiState sysUiFlagsContainer,
CommandQueue commandQueue,
- @Main Handler mainHandler,
+ @Main Executor mainExecutor,
ConfigurationController configurationController,
NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
@@ -133,7 +135,7 @@ public class NavigationBarControllerImpl implements
SecureSettings secureSettings,
DisplayTracker displayTracker) {
mContext = context;
- mHandler = mainHandler;
+ mExecutor = mainExecutor;
mNavigationBarComponentFactory = navigationBarComponentFactory;
mSecureSettings = secureSettings;
mDisplayTracker = displayTracker;
@@ -193,7 +195,7 @@ public class NavigationBarControllerImpl implements
mNavMode = mode;
updateAccessibilityButtonModeIfNeeded();
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
// create/destroy nav bar based on nav mode only in unfolded state
if (oldMode != mNavMode) {
updateNavbarForTaskbar();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
index 5b7eb454597c..deb0fed0ffc8 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
@@ -20,14 +20,15 @@ import android.appwidget.AppWidgetManager
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.compose.ComposeFacade.isComposeAvailable
-import com.android.systemui.compose.ComposeFacade.setPeopleSpaceActivityContent
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.people.ui.compose.PeopleScreen
import com.android.systemui.people.ui.view.PeopleViewBinder
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
@@ -65,13 +66,11 @@ constructor(
}
// Set the content of the activity, using either the View or Compose implementation.
- if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE) && isComposeAvailable()) {
+ if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE)) {
Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity")
- setPeopleSpaceActivityContent(
- activity = this,
- viewModel,
- onResult = { finishActivity(it) },
- )
+ setContent {
+ PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) }
+ }
} else {
Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity")
val view = PeopleViewBinder.create(this)
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
index e1d1ec207938..5432793d8117 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
@@ -20,20 +20,24 @@ data class WakefulnessModel(
* the [KeyguardTransitionInteractor].
*/
internal val internalWakefulnessState: WakefulnessState = WakefulnessState.AWAKE,
-
val lastWakeReason: WakeSleepReason = WakeSleepReason.OTHER,
val lastSleepReason: WakeSleepReason = WakeSleepReason.OTHER,
- /**
+ /**
* Whether the power button double tap gesture was triggered since the last time went to sleep.
* If this value is true while [isAsleep]=true, it means we'll be waking back up shortly. If it
* is true while [isAwake]=true, it means we're awake because of the button gesture.
*
- * This value remains true until the next time [isAsleep]=true.
+ * This value remains true until the next time [isAsleep]=true, since it would otherwise be
+ * totally arbitrary at what point we decide the gesture was no longer "triggered". Since a
+ * sleep event is guaranteed to arrive prior to the next power button gesture (as the first tap
+ * of the double tap always begins a sleep transition), this will always be reset to false prior
+ * to a subsequent power gesture.
*/
val powerButtonLaunchGestureTriggered: Boolean = false,
) {
- fun isAwake() = internalWakefulnessState == WakefulnessState.AWAKE ||
+ fun isAwake() =
+ internalWakefulnessState == WakefulnessState.AWAKE ||
internalWakefulnessState == WakefulnessState.STARTING_TO_WAKE
fun isAsleep() = !isAwake()
@@ -48,11 +52,10 @@ data class WakefulnessModel(
fun isAsleepFrom(wakeSleepReason: WakeSleepReason) =
isAsleep() && lastSleepReason == wakeSleepReason
- fun isAwakeOrAsleepFrom(reason: WakeSleepReason) =
- isAsleepFrom(reason) || isAwakeFrom(reason)
+ fun isAwakeOrAsleepFrom(reason: WakeSleepReason) = isAsleepFrom(reason) || isAwakeFrom(reason)
fun isAwakeFromTapOrGesture(): Boolean {
- return isAwake() && (lastWakeReason == WakeSleepReason.TAP ||
- lastWakeReason == WakeSleepReason.GESTURE)
+ return isAwake() &&
+ (lastWakeReason == WakeSleepReason.TAP || lastWakeReason == WakeSleepReason.GESTURE)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 741336277119..a000d63a2ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -47,7 +47,6 @@ import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ShadeInterpolation;
-import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -301,8 +300,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
private void bindFooterActionsView(View root) {
LinearLayout footerActionsView = root.findViewById(R.id.qs_footer_actions);
- if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)
- || !ComposeFacade.INSTANCE.isComposeAvailable()) {
+ if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)) {
Log.d(TAG, "Binding the View implementation of the QS footer actions");
mFooterActionsView = footerActionsView;
mFooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
@@ -312,7 +310,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
// Compose is available, so let's use the Compose implementation of the footer actions.
Log.d(TAG, "Binding the Compose implementation of the QS footer actions");
- View composeView = ComposeFacade.INSTANCE.createFooterActionsView(root.getContext(),
+ View composeView = QSUtils.createFooterActionsView(root.getContext(),
mQSFooterActionsViewModel, mListeningAndVisibilityLifecycleOwner);
mFooterActionsView = composeView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
index e42264f24e92..15c3f271469d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
@@ -1,7 +1,13 @@
package com.android.systemui.qs
import android.content.Context
+import android.view.View
+import androidx.lifecycle.LifecycleOwner
+import com.android.compose.theme.PlatformTheme
+import com.android.compose.ui.platform.DensityAwareComposeView
import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.qs.footer.ui.compose.FooterActions
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.util.LargeScreenUtils.shouldUseLargeScreenShadeHeader
object QSUtils {
@@ -21,4 +27,15 @@ object QSUtils {
SystemBarUtils.getQuickQsOffsetHeight(context)
}
}
-} \ No newline at end of file
+
+ @JvmStatic
+ fun createFooterActionsView(
+ context: Context,
+ viewModel: FooterActionsViewModel,
+ qsVisibilityLifecycleOwner: LifecycleOwner,
+ ): View {
+ return DensityAwareComposeView(context).apply {
+ setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
index 168fa088cff2..15b8cfb3834d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
@@ -39,6 +39,7 @@ object SubtitleArrayMapping {
subtitleIdsMap["cast"] = R.array.tile_states_cast
subtitleIdsMap["night"] = R.array.tile_states_night
subtitleIdsMap["screenrecord"] = R.array.tile_states_screenrecord
+ subtitleIdsMap["record_issue"] = R.array.tile_states_record_issue
subtitleIdsMap["reverse"] = R.array.tile_states_reverse
subtitleIdsMap["reduce_brightness"] = R.array.tile_states_reduce_brightness
subtitleIdsMap["cameratoggle"] = R.array.tile_states_cameratoggle
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index b1e2467e007b..20f3c4d9735b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -85,7 +85,13 @@ constructor(
override fun getTileLabel(): CharSequence = mContext.getString(R.string.qs_record_issue_label)
- override fun isAvailable(): Boolean = recordIssueQsTile()
+ /**
+ * There are SELinux constraints that are stopping this tile from reaching production builds.
+ * Once those are resolved, this condition will be removed, but the solution (of properly
+ * creating a distince SELinux context for com.android.systemui) is complex and will take time
+ * to implement.
+ */
+ override fun isAvailable(): Boolean = android.os.Build.IS_DEBUGGABLE && recordIssueQsTile()
override fun newTileState(): QSTile.BooleanState =
QSTile.BooleanState().apply {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 0dd0a60b128a..52cf4ec57e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -481,7 +481,8 @@ public class InternetDialogDelegate implements
mSecondaryMobileTitleText.setTextAppearance(
R.style.TextAppearance_InternetDialog_Active);
- TextView mSecondaryMobileSummaryText = mDialogView.requireViewById(R.id.secondary_mobile_summary);
+ TextView mSecondaryMobileSummaryText =
+ mDialogView.requireViewById(R.id.secondary_mobile_summary);
summary = getMobileNetworkSummary(autoSwitchNonDdsSubId);
if (!TextUtils.isEmpty(summary)) {
mSecondaryMobileSummaryText.setText(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
new file mode 100644
index 000000000000..736e1a5cb9b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.rotation.domain.interactor
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.os.UserHandle
+import com.android.systemui.camera.data.repository.CameraAutoRotateRepository
+import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepository
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.RotationLockController
+import com.android.systemui.util.kotlin.isBatteryPowerSaveEnabled
+import com.android.systemui.util.kotlin.isRotationLockEnabled
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+
+/** Observes rotation lock state changes providing the [RotationLockTileModel]. */
+class RotationLockTileDataInteractor
+@Inject
+constructor(
+ private val rotationLockController: RotationLockController,
+ private val batteryController: BatteryController,
+ private val cameraAutoRotateRepository: CameraAutoRotateRepository,
+ private val cameraSensorPrivacyRepository: CameraSensorPrivacyRepository,
+ private val packageManager: PackageManager,
+ @Main private val resources: Resources,
+) : QSTileDataInteractor<RotationLockTileModel> {
+
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<RotationLockTileModel> =
+ combine(
+ rotationLockController.isRotationLockEnabled(),
+ cameraSensorPrivacyRepository.isEnabled(user),
+ batteryController.isBatteryPowerSaveEnabled(),
+ cameraAutoRotateRepository.isCameraAutoRotateSettingEnabled(user)
+ ) {
+ isRotationLockEnabled,
+ isCamPrivacySensorEnabled,
+ isBatteryPowerSaveEnabled,
+ isCameraAutoRotateEnabled,
+ ->
+ RotationLockTileModel(
+ isRotationLockEnabled,
+ isCameraRotationEnabled(
+ isBatteryPowerSaveEnabled,
+ isCamPrivacySensorEnabled,
+ isCameraAutoRotateEnabled
+ ),
+ )
+ }
+
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
+
+ private fun hasSufficientPermission(): Boolean {
+ val rotationPackage: String = packageManager.rotationResolverPackageName
+ return rotationPackage != null &&
+ packageManager.checkPermission(Manifest.permission.CAMERA, rotationPackage) ==
+ PackageManager.PERMISSION_GRANTED
+ }
+
+ private fun isCameraRotationEnabled(
+ isBatteryPowerSaverModeOn: Boolean,
+ isCameraSensorPrivacyEnabled: Boolean,
+ isCameraAutoRotateEnabled: Boolean
+ ): Boolean =
+ resources.getBoolean(com.android.internal.R.bool.config_allowRotationResolver) &&
+ !isBatteryPowerSaverModeOn &&
+ !isCameraSensorPrivacyEnabled &&
+ hasSufficientPermission() &&
+ isCameraAutoRotateEnabled
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
new file mode 100644
index 000000000000..8530926e68e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.rotation.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.statusbar.policy.RotationLockController
+import javax.inject.Inject
+
+/** Handles rotation lock tile clicks. */
+class RotationLockTileUserActionInteractor
+@Inject
+constructor(
+ private val controller: RotationLockController,
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+) : QSTileUserActionInteractor<RotationLockTileModel> {
+
+ override suspend fun handleInput(input: QSTileInput<RotationLockTileModel>) {
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ controller.setRotationLocked(!data.isRotationLocked, CALLER)
+ }
+ is QSTileUserAction.LongClick -> {
+ qsTileIntentUserActionHandler.handle(
+ action.view,
+ Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS)
+ )
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val CALLER = "QSTileUserActionInteractor#handleInput"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKey.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/model/RotationLockTileModel.kt
index 87332ae8e084..32e6cb8cb52c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/model/RotationLockTileModel.kt
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.scene.shared.model
+package com.android.systemui.qs.tiles.impl.rotation.domain.model
-/**
- * Key for a transition. This can be used to specify which transition spec should be used when
- * starting the transition between two scenes.
- */
-data class TransitionKey(
- val debugName: String,
- val identity: Any = Object(),
+/** Model for rotation lock tile */
+class RotationLockTileModel(
+ val isRotationLocked: Boolean,
+ val isCameraRotationEnabled: Boolean,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
new file mode 100644
index 000000000000..070cdef336e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.rotation.ui.mapper
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.DevicePostureController
+import javax.inject.Inject
+
+/** Maps [RotationLockTileModel] to [QSTileState]. */
+class RotationLockTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Resources.Theme,
+ private val devicePostureController: DevicePostureController
+) : QSTileDataToStateMapper<RotationLockTileModel> {
+ override fun map(config: QSTileConfig, data: RotationLockTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ this.label = resources.getString(R.string.quick_settings_rotation_unlocked_label)
+ this.contentDescription =
+ resources.getString(R.string.accessibility_quick_settings_rotation)
+
+ if (data.isRotationLocked) {
+ activationState = QSTileState.ActivationState.INACTIVE
+ this.secondaryLabel = EMPTY_SECONDARY_STRING
+ this.icon = {
+ Icon.Loaded(
+ resources.getDrawable(R.drawable.qs_auto_rotate_icon_off, theme),
+ contentDescription = null
+ )
+ }
+ } else {
+ activationState = QSTileState.ActivationState.ACTIVE
+ this.secondaryLabel =
+ if (data.isCameraRotationEnabled) {
+ resources.getString(R.string.rotation_lock_camera_rotation_on)
+ } else {
+ EMPTY_SECONDARY_STRING
+ }
+ this.icon = {
+ Icon.Loaded(
+ resources.getDrawable(R.drawable.qs_auto_rotate_icon_on, theme),
+ contentDescription = null
+ )
+ }
+ }
+ if (isDeviceFoldable()) {
+ this.secondaryLabel = getSecondaryLabelWithPosture(this.activationState)
+ }
+ this.stateDescription = this.secondaryLabel
+ this.sideViewIcon = QSTileState.SideViewIcon.None
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ }
+
+ private fun isDeviceFoldable(): Boolean {
+ val intArray = resources.getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
+ return intArray.isNotEmpty()
+ }
+
+ private fun getSecondaryLabelWithPosture(activationState: QSTileState.ActivationState): String {
+ val stateNames = resources.getStringArray(R.array.tile_states_rotation)
+ val stateName =
+ stateNames[
+ if (activationState == QSTileState.ActivationState.ACTIVE) ON_INDEX else OFF_INDEX]
+ val posture =
+ if (
+ devicePostureController.devicePosture ==
+ DevicePostureController.DEVICE_POSTURE_CLOSED
+ )
+ resources.getString(R.string.quick_settings_rotation_posture_folded)
+ else resources.getString(R.string.quick_settings_rotation_posture_unfolded)
+
+ return resources.getString(
+ R.string.rotation_tile_with_posture_secondary_label_template,
+ stateName,
+ posture
+ )
+ }
+
+ private companion object {
+ const val EMPTY_SECONDARY_STRING = ""
+ const val OFF_INDEX = 1
+ const val ON_INDEX = 2
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 17454a97f5d2..34f66b85def1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -17,14 +17,16 @@
package com.android.systemui.qs.ui.viewmodel
import androidx.lifecycle.LifecycleOwner
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
-import com.android.systemui.scene.shared.model.Direction
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import java.util.concurrent.atomic.AtomicBoolean
@@ -45,13 +47,11 @@ constructor(
val destinationScenes =
qsSceneAdapter.isCustomizing.map { customizing ->
if (customizing) {
- mapOf<UserAction, UserActionResult>(
- UserAction.Back to UserActionResult(SceneKey.QuickSettings)
- )
+ mapOf<UserAction, UserActionResult>(Back to UserActionResult(Scenes.QuickSettings))
} else {
mapOf(
- UserAction.Back to UserActionResult(SceneKey.Shade),
- UserAction.Swipe(Direction.UP) to UserActionResult(SceneKey.Shade),
+ Back to UserActionResult(Scenes.Shade),
+ Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 80f11f1e1874..1c07d00e4195 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -40,7 +40,7 @@ import com.android.systemui.flags.Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPR
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.SessionCreationSource
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
-import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate
import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
import com.android.systemui.screenrecord.RecordingService
@@ -65,6 +65,7 @@ constructor(
private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>,
private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
private val userFileManager: UserFileManager,
+ private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate,
@Assisted private val onStarted: Runnable,
) : SystemUIDialog.Delegate {
@@ -124,7 +125,7 @@ constructor(
.isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
) {
mainExecutor.execute {
- ScreenCaptureDisabledDialog(context).show()
+ screenCaptureDisabledDialogDelegate.createDialog().show()
screenRecordSwitch.isChecked = false
}
return@execute
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
new file mode 100644
index 000000000000..eb64dd609a72
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.rotationlock
+
+import com.android.systemui.camera.CameraRotationModule
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor
+import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
+import com.android.systemui.qs.tiles.impl.rotation.ui.mapper.RotationLockTileMapper
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.res.R
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module(includes = [CameraRotationModule::class])
+interface RotationLockNewModule {
+ companion object {
+ private const val ROTATION_TILE_SPEC = "rotation"
+
+ /** Inject rotation tile config */
+ @Provides
+ @IntoMap
+ @StringKey(ROTATION_TILE_SPEC)
+ fun provideRotationTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(ROTATION_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_auto_rotate_icon_off,
+ labelRes = R.string.quick_settings_rotation_unlocked_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ )
+
+ /** Inject Rotation tile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(ROTATION_TILE_SPEC)
+ fun provideRotationTileViewModel(
+ factory: QSTileViewModelFactory.Static<RotationLockTileModel>,
+ mapper: RotationLockTileMapper,
+ stateInteractor: RotationLockTileDataInteractor,
+ userActionInteractor: RotationLockTileUserActionInteractor
+ ): QSTileViewModel =
+ factory.create(
+ TileSpec.create(ROTATION_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 356eb858d78f..afd0746f4696 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -18,7 +18,7 @@ package com.android.systemui.scene
import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import dagger.Module
import dagger.Provides
@@ -44,11 +44,11 @@ object KeyguardlessSceneContainerFrameworkModule {
// last one is top-most.
sceneKeys =
listOf(
- SceneKey.Gone,
- SceneKey.QuickSettings,
- SceneKey.Shade,
+ Scenes.Gone,
+ Scenes.QuickSettings,
+ Scenes.Shade,
),
- initialSceneKey = SceneKey.Gone,
+ initialSceneKey = Scenes.Gone,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index c7d3a4af24c9..62b0914fab79 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -17,11 +17,12 @@
package com.android.systemui.scene
import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -34,6 +35,7 @@ import dagger.multibindings.IntoMap
[
BouncerSceneModule::class,
CommunalSceneModule::class,
+ ComposeBouncerFlagsModule::class,
EmptySceneModule::class,
GoneSceneModule::class,
LockscreenSceneModule::class,
@@ -65,14 +67,14 @@ interface SceneContainerFrameworkModule {
// last one is top-most.
sceneKeys =
listOf(
- SceneKey.Gone,
- SceneKey.Communal,
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
- SceneKey.QuickSettings,
- SceneKey.Shade,
+ Scenes.Gone,
+ Scenes.Communal,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
+ Scenes.QuickSettings,
+ Scenes.Shade,
),
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
index c10e51b68ba2..0665c9e1b802 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
@@ -18,7 +18,7 @@ package com.android.systemui.scene
import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import dagger.Module
import dagger.Provides
@@ -44,11 +44,11 @@ object ShadelessSceneContainerFrameworkModule {
// last one is top-most.
sceneKeys =
listOf(
- SceneKey.Gone,
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
),
- initialSceneKey = SceneKey.Lockscreen,
+ initialSceneKey = Scenes.Lockscreen,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index e60dff183148..994b01216c22 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -18,12 +18,12 @@
package com.android.systemui.scene.data.repository
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSource
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.TransitionKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt
index 36350f8af455..3a5ea5c6a064 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/PanelExpansionInteractor.kt
@@ -18,10 +18,11 @@
package com.android.systemui.scene.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,7 +54,7 @@ constructor(
when (state) {
is ObservableTransitionState.Idle ->
flowOf(
- if (state.scene != SceneKey.Gone) {
+ if (state.scene != Scenes.Gone) {
// When resting on a non-Gone scene, the panel is fully expanded.
1f
} else {
@@ -64,7 +65,7 @@ constructor(
)
is ObservableTransitionState.Transition ->
when {
- state.fromScene == SceneKey.Gone ->
+ state.fromScene == Scenes.Gone ->
if (state.toScene.isExpandable()) {
// Moving from Gone to a scene that can animate-expand has a
// panel
@@ -77,7 +78,7 @@ constructor(
// the panel fully expanded.
flowOf(1f)
}
- state.toScene == SceneKey.Gone ->
+ state.toScene == Scenes.Gone ->
if (state.fromScene.isExpandable()) {
// Moving to Gone from a scene that can animate-expand has a
// panel
@@ -99,6 +100,6 @@ constructor(
}
private fun SceneKey.isExpandable(): Boolean {
- return this == SceneKey.Shade || this == SceneKey.QuickSettings
+ return this == Scenes.Shade || this == Scenes.QuickSettings
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 6b7c672fbfe0..75bf131afdf9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -16,14 +16,15 @@
package com.android.systemui.scene.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.logger.SceneLogger
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.TransitionKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.pairwiseBy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -155,12 +156,13 @@ constructor(
* desired scene. Once enough of the transition has occurred, the [currentScene] will become
* [toScene] (unless the transition is canceled by user action or another call to this method).
*/
+ @JvmOverloads
fun changeScene(
toScene: SceneKey,
loggingReason: String,
transitionKey: TransitionKey? = null,
) {
- check(toScene != SceneKey.Gone || deviceUnlockedInteractor.isDeviceUnlocked.value) {
+ check(toScene != Scenes.Gone || deviceUnlockedInteractor.isDeviceUnlocked.value) {
"Cannot change to the Gone scene while the device is locked. Logging reason for scene" +
" change was: $loggingReason"
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
index 1c37908235bd..c736707ecd2d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -24,8 +25,7 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.init.NotificationsController
@@ -77,12 +77,12 @@ constructor(
.map { state ->
when (state) {
is ObservableTransitionState.Idle ->
- state.scene == SceneKey.Shade || state.scene == SceneKey.Lockscreen
+ state.scene == Scenes.Shade || state.scene == Scenes.Lockscreen
is ObservableTransitionState.Transition ->
- state.toScene == SceneKey.Shade ||
- state.toScene == SceneKey.Lockscreen ||
- state.fromScene == SceneKey.Shade ||
- state.fromScene == SceneKey.Lockscreen
+ state.toScene == Scenes.Shade ||
+ state.toScene == Scenes.Lockscreen ||
+ state.fromScene == Scenes.Shade ||
+ state.fromScene == Scenes.Lockscreen
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 034f87f4c72f..6df57edd34c3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -19,6 +19,8 @@
package com.android.systemui.scene.domain.startable
import android.app.StatusBarManager
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -41,8 +43,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.logger.SceneLogger
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
@@ -140,14 +141,14 @@ constructor(
.mapNotNull { state ->
when (state) {
is ObservableTransitionState.Idle -> {
- if (state.scene != SceneKey.Gone) {
+ if (state.scene != Scenes.Gone) {
true to "scene is not Gone"
} else {
false to "scene is Gone"
}
}
is ObservableTransitionState.Transition -> {
- if (state.fromScene == SceneKey.Gone) {
+ if (state.fromScene == Scenes.Gone) {
true to "scene transitioning away from Gone"
} else {
null
@@ -180,9 +181,9 @@ constructor(
applicationScope.launch {
// TODO (b/308001302): Move this to a bouncer specific interactor.
bouncerInteractor.onImeHiddenByUser.collectLatest {
- if (sceneInteractor.currentScene.value == SceneKey.Bouncer) {
+ if (sceneInteractor.currentScene.value == Scenes.Bouncer) {
sceneInteractor.changeScene(
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
loggingReason = "IME hidden",
)
}
@@ -196,13 +197,13 @@ constructor(
when {
isAnySimLocked -> {
switchToScene(
- targetSceneKey = SceneKey.Bouncer,
+ targetSceneKey = Scenes.Bouncer,
loggingReason = "Need to authenticate locked SIM card."
)
}
isUnlocked && canSwipeToEnter == false -> {
switchToScene(
- targetSceneKey = SceneKey.Gone,
+ targetSceneKey = Scenes.Gone,
loggingReason =
"All SIM cards unlocked and device already" +
" unlocked and lockscreen doesn't require a swipe to dismiss."
@@ -210,7 +211,7 @@ constructor(
}
else -> {
switchToScene(
- targetSceneKey = SceneKey.Lockscreen,
+ targetSceneKey = Scenes.Lockscreen,
loggingReason =
"All SIM cards unlocked and device still locked" +
" or lockscreen still requires a swipe to dismiss."
@@ -231,8 +232,8 @@ constructor(
transitionState.toScene,
)
}
- val isOnLockscreen = renderedScenes.contains(SceneKey.Lockscreen)
- val isOnBouncer = renderedScenes.contains(SceneKey.Bouncer)
+ val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen)
+ val isOnBouncer = renderedScenes.contains(Scenes.Bouncer)
if (!isUnlocked) {
return@mapNotNull if (isOnLockscreen || isOnBouncer) {
// Already on lockscreen or bouncer, no need to change scenes.
@@ -240,7 +241,7 @@ constructor(
} else {
// The device locked while on a scene that's not Lockscreen or Bouncer,
// go to Lockscreen.
- SceneKey.Lockscreen to
+ Scenes.Lockscreen to
"device locked in non-Lockscreen and non-Bouncer scene"
}
}
@@ -250,7 +251,7 @@ constructor(
when {
isOnBouncer ->
// When the device becomes unlocked in Bouncer, go to Gone.
- SceneKey.Gone to "device was unlocked in Bouncer scene"
+ Scenes.Gone to "device was unlocked in Bouncer scene"
isOnLockscreen ->
// The lockscreen should be dismissed automatically in 2 scenarios:
// 1. When face auth bypass is enabled and authentication happens while
@@ -262,11 +263,11 @@ constructor(
// authentication attempt.
when {
isBypassEnabled ->
- SceneKey.Gone to
+ Scenes.Gone to
"device has been unlocked on lockscreen with bypass" +
" enabled"
canSwipeToEnter == false ->
- SceneKey.Gone to
+ Scenes.Gone to
"device has been unlocked on lockscreen using an active" +
" authentication mechanism"
else -> null
@@ -287,7 +288,7 @@ constructor(
powerInteractor.isAsleep.collect { isAsleep ->
if (isAsleep) {
switchToScene(
- targetSceneKey = SceneKey.Lockscreen,
+ targetSceneKey = Scenes.Lockscreen,
loggingReason = "device is starting to sleep",
)
} else {
@@ -295,7 +296,7 @@ constructor(
val isUnlocked = deviceEntryInteractor.isUnlocked.value
if (isUnlocked && canSwipeToEnter == false) {
switchToScene(
- targetSceneKey = SceneKey.Gone,
+ targetSceneKey = Scenes.Gone,
loggingReason =
"device is waking up while unlocked without the ability" +
" to swipe up on lockscreen to enter.",
@@ -305,7 +306,7 @@ constructor(
AuthenticationMethodModel.Sim
) {
switchToScene(
- targetSceneKey = SceneKey.Bouncer,
+ targetSceneKey = Scenes.Bouncer,
loggingReason = "device is starting to wake up with a locked sim"
)
}
@@ -370,7 +371,7 @@ constructor(
applicationScope.launch {
sceneInteractor.currentScene
- .map { it == SceneKey.Bouncer }
+ .map { it == Scenes.Bouncer }
.distinctUntilChanged()
.collect { switchedToBouncerScene ->
if (switchedToBouncerScene) {
@@ -390,7 +391,7 @@ constructor(
falsingManager.addFalsingBeliefListener(listener)
awaitClose { falsingManager.removeFalsingBeliefListener(listener) }
}
- .collect { switchToScene(SceneKey.Lockscreen, "Falsing detected.") }
+ .collect { switchToScene(Scenes.Lockscreen, "Falsing detected.") }
}
}
@@ -403,7 +404,7 @@ constructor(
}
.distinctUntilChanged()
.collect { sceneKey ->
- windowController.setNotificationShadeFocusable(sceneKey != SceneKey.Gone)
+ windowController.setNotificationShadeFocusable(sceneKey != Scenes.Gone)
}
}
}
@@ -427,9 +428,9 @@ constructor(
//
// This is done here in order to match the legacy
// implementation. The real reason why is lost to lore and myth.
- SceneKey.Lockscreen -> true
- SceneKey.Bouncer -> false
- SceneKey.Shade -> false
+ Scenes.Lockscreen -> true
+ Scenes.Bouncer -> false
+ Scenes.Shade -> false
else -> null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 8408c51c86dc..1808d98cd692 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -24,7 +24,6 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.Flags.sceneContainer
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED
@@ -47,9 +46,8 @@ object SceneContainerFlag {
keyguardBottomAreaRefactor() &&
migrateClocksToBlueprint() &&
ComposeLockscreen.isEnabled &&
- MediaInSceneContainerFlag.isEnabled &&
- // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
- ComposeFacade.isComposeAvailable()
+ MediaInSceneContainerFlag.isEnabled
+ // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
/**
* The main static flag, SCENE_CONTAINER_ENABLED. This is an explicit static flag check that
@@ -74,11 +72,7 @@ object SceneContainerFlag {
/** The full set of requirements for SceneContainer */
inline fun getAllRequirements(): Sequence<FlagToken> {
- val composeRequirement =
- FlagToken("ComposeFacade.isComposeAvailable()", ComposeFacade.isComposeAvailable())
- return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) +
- getSecondaryFlags() +
- composeRequirement
+ return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) + getSecondaryFlags()
}
/** Return all dependencies of this flag in pairs where [Pair.first] depends on [Pair.second] */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index cbf7b3e7a971..f44779ade8db 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -16,10 +16,10 @@
package com.android.systemui.scene.shared.logger
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.SceneFrameworkLog
-import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer: LogBuffer) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
deleted file mode 100644
index f704894e56e2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.shared.model
-
-import kotlinx.coroutines.flow.Flow
-
-/**
- * This is a fork of a class by the same name in the `com.android.compose.animation.scene` package.
- *
- * TODO(b/293899074): remove this fork, once we can compile Compose into System UI.
- */
-sealed class ObservableTransitionState {
- /** No transition/animation is currently running. */
- data class Idle(val scene: SceneKey) : ObservableTransitionState()
-
- /** There is a transition animating between two scenes. */
- data class Transition(
- val fromScene: SceneKey,
- val toScene: SceneKey,
- val progress: Flow<Float>,
-
- /**
- * Whether the transition was originally triggered by user input rather than being
- * programmatic. If this value is initially true, it will remain true until the transition
- * fully completes, even if the user input that triggered the transition has ended. Any
- * sub-transitions launched by this one will inherit this value. For example, if the user
- * drags a pointer but does not exceed the threshold required to transition to another
- * scene, this value will remain true after the pointer is no longer touching the screen and
- * will be true in any transition created to animate back to the original position.
- */
- val isInitiatedByUserInput: Boolean,
-
- /**
- * Whether user input is currently driving the transition. For example, if a user is
- * dragging a pointer, this emits true. Once they lift their finger, this emits false while
- * the transition completes/settles.
- */
- val isUserInputOngoing: Flow<Boolean>,
- ) : ObservableTransitionState()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 05056c133ca3..939d5bc6588e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -16,6 +16,9 @@
package com.android.systemui.scene.shared.model
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import kotlinx.coroutines.flow.StateFlow
/**
@@ -53,39 +56,3 @@ interface Scene {
*/
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>>
}
-
-/** Enumerates all scene framework supported user actions. */
-sealed interface UserAction {
-
- /** The user is scrolling, dragging, swiping, or flinging. */
- data class Swipe(
- /** The direction of the swipe. */
- val direction: Direction,
- /**
- * The edge from which the swipe originated or `null`, if the swipe didn't start close to an
- * edge.
- */
- val fromEdge: Edge? = null,
- /** The number of pointers that were used (for example, one or two fingers). */
- val pointerCount: Int = 1,
- ) : UserAction
-
- /** The user has hit the back button or performed the back navigation gesture. */
- data object Back : UserAction
-}
-
-/** Enumerates all known "cardinal" directions for user actions. */
-enum class Direction {
- LEFT,
- UP,
- RIGHT,
- DOWN,
-}
-
-/** Enumerates all known edges from which a swipe can start. */
-enum class Edge {
- LEFT,
- TOP,
- RIGHT,
- BOTTOM,
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
index 8204edc33fe4..53cdaaab7478 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.shared.model
+import com.android.compose.animation.scene.SceneKey
+
/** Models the configuration of the scene container. */
data class SceneContainerConfig(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt
index f7b45e547b7f..0e078d5d8064 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.shared.model
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import kotlinx.coroutines.flow.StateFlow
/** Defines interface for classes that provide access to scene state. */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
index a50830c1f212..69dce83b7136 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
@@ -18,6 +18,8 @@
package com.android.systemui.scene.shared.model
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
index 609d2b93a36e..73fcca8c6b7f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
@@ -16,41 +16,37 @@
package com.android.systemui.scene.shared.model
+import com.android.compose.animation.scene.SceneKey
+
/**
* Keys of all known scenes.
*
* PLEASE KEEP THE KEYS SORTED ALPHABETICALLY.
*/
-sealed class SceneKey(
- private val loggingName: String,
-) {
+object Scenes {
/**
* The bouncer is the scene that displays authentication challenges like PIN, password, or
* pattern.
*/
- object Bouncer : SceneKey("bouncer")
+ @JvmField val Bouncer = SceneKey("bouncer")
/** The communal scene shows the glanceable hub when device is locked and docked. */
- object Communal : SceneKey("communal")
+ @JvmField val Communal = SceneKey("communal")
/**
* "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
* content from the scene framework.
*/
- object Gone : SceneKey("gone")
+ @JvmField val Gone = SceneKey("gone")
/** The lockscreen is the scene that shows when the device is locked. */
- object Lockscreen : SceneKey("lockscreen")
+ @JvmField val Lockscreen = SceneKey("lockscreen")
/** The quick settings scene shows the quick setting tiles. */
- object QuickSettings : SceneKey("quick_settings")
+ @JvmField val QuickSettings = SceneKey("quick_settings")
/**
* The shade is the scene whose primary purpose is to show a scrollable list of notifications.
*/
- object Shade : SceneKey("shade")
-
- override fun toString(): String {
- return loggingName
- }
+ @JvmField val Shade = SceneKey("shade")
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
index 926878c1870e..b91dd0451808 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.shared.model
+import com.android.compose.animation.scene.TransitionKey
+
/**
* Defines all known named transitions.
*
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index ee76c0582b9d..7c31ca269eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -16,28 +16,43 @@
package com.android.systemui.scene.ui.view
+import android.content.Context
+import android.graphics.Point
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.compose.ComposeFacade
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.theme.PlatformTheme
+import com.android.internal.policy.ScreenDecorationsUtils
+import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
+import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
+import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.SceneContainer
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
object SceneWindowRootViewBinder {
@@ -83,7 +98,7 @@ object SceneWindowRootViewBinder {
)
view.addView(
- ComposeFacade.createSceneContainerView(
+ createSceneContainerView(
scope = this,
context = view.context,
viewModel = viewModel,
@@ -120,4 +135,74 @@ object SceneWindowRootViewBinder {
}
}
}
+
+ private fun createSceneContainerView(
+ scope: CoroutineScope,
+ context: Context,
+ viewModel: SceneContainerViewModel,
+ windowInsets: StateFlow<WindowInsets?>,
+ sceneByKey: Map<SceneKey, Scene>,
+ dataSourceDelegator: SceneDataSourceDelegator,
+ ): View {
+ return ComposeView(context).apply {
+ setContent {
+ PlatformTheme {
+ ScreenDecorProvider(
+ displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
+ screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+ ) {
+ SceneContainer(
+ viewModel = viewModel,
+ sceneByKey =
+ sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
+ dataSourceDelegator = dataSourceDelegator,
+ )
+ }
+ }
+ }
+ }
+ }
+
+ // TODO(b/298525212): remove once Compose exposes window inset bounds.
+ private fun displayCutoutFromWindowInsets(
+ scope: CoroutineScope,
+ context: Context,
+ windowInsets: StateFlow<WindowInsets?>,
+ ): StateFlow<DisplayCutout> =
+ windowInsets
+ .map {
+ val boundingRect = it?.displayCutout?.boundingRectTop
+ val width = boundingRect?.let { boundingRect.right - boundingRect.left } ?: 0
+ val left = boundingRect?.left?.toDp(context) ?: 0.dp
+ val top = boundingRect?.top?.toDp(context) ?: 0.dp
+ val right = boundingRect?.right?.toDp(context) ?: 0.dp
+ val bottom = boundingRect?.bottom?.toDp(context) ?: 0.dp
+ val location =
+ when {
+ width <= 0f -> CutoutLocation.NONE
+ left <= 0.dp -> CutoutLocation.LEFT
+ right >= getDisplayWidth(context) -> CutoutLocation.RIGHT
+ else -> CutoutLocation.CENTER
+ }
+ DisplayCutout(
+ left,
+ top,
+ right,
+ bottom,
+ location,
+ )
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout())
+
+ // TODO(b/298525212): remove once Compose exposes window inset bounds.
+ private fun getDisplayWidth(context: Context): Dp {
+ val point = Point()
+ checkNotNull(context.display).getRealSize(point)
+ return point.x.dp
+ }
+
+ // TODO(b/298525212): remove once Compose exposes window inset bounds.
+ private fun Int.toDp(context: Context): Dp {
+ return (this.toFloat() / context.resources.displayMetrics.density).dp
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index 4c2c97981702..ea19020d84d4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -25,7 +25,7 @@ import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updateMargins
-import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.compose.ComposeInitializer
import com.android.systemui.res.R
/** A view that can serve as the root of the main SysUI window. */
@@ -45,16 +45,16 @@ open class WindowRootView(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- if (ComposeFacade.isComposeAvailable() && isRoot()) {
- ComposeFacade.composeInitializer().onAttachedToWindow(this)
+ if (isRoot()) {
+ ComposeInitializer.onAttachedToWindow(this)
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
- if (ComposeFacade.isComposeAvailable() && isRoot()) {
- ComposeFacade.composeInitializer().onDetachedFromWindow(this)
+ if (isRoot()) {
+ ComposeInitializer.onDetachedFromWindow(this)
}
}
@@ -71,7 +71,6 @@ open class WindowRootView(
override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets? {
val insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
- val displayCutout = rootWindowInsets.displayCutout
if (fitsSystemWindows) {
val paddingChanged = insets.top != paddingTop || insets.bottom != paddingBottom
@@ -79,23 +78,22 @@ open class WindowRootView(
if (paddingChanged) {
setPadding(0, 0, 0, 0)
}
-
- val pairInsets: Pair<Int, Int> =
- layoutInsetsController.getinsets(windowInsets, displayCutout)
- leftInset = pairInsets.first
- rightInset = pairInsets.second
- applyMargins()
} else {
val changed =
paddingLeft != 0 || paddingRight != 0 || paddingTop != 0 || paddingBottom != 0
if (changed) {
setPadding(0, 0, 0, 0)
}
-
- leftInset = 0
- rightInset = 0
}
+ leftInset = 0
+ rightInset = 0
+ val displayCutout = rootWindowInsets.displayCutout
+ val pairInsets: Pair<Int, Int> =
+ layoutInsetsController.getinsets(windowInsets, displayCutout)
+ leftInset = pairInsets.first
+ rightInset = pairInsets.second
+ applyMargins()
return windowInsets
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 91861aa5c29a..231b28424691 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -17,13 +17,14 @@
package com.android.systemui.scene.ui.viewmodel
import android.view.MotionEvent
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -96,10 +97,10 @@ constructor(
fun canChangeScene(toScene: SceneKey): Boolean {
val interactionTypeOrNull =
when (toScene) {
- SceneKey.Bouncer -> Classifier.BOUNCER_UNLOCK
- SceneKey.Gone -> Classifier.UNLOCK
- SceneKey.Shade -> Classifier.NOTIFICATION_DRAG_DOWN
- SceneKey.QuickSettings -> Classifier.QUICK_SETTINGS
+ Scenes.Bouncer -> Classifier.BOUNCER_UNLOCK
+ Scenes.Gone -> Classifier.UNLOCK
+ Scenes.Shade -> Classifier.NOTIFICATION_DRAG_DOWN
+ Scenes.QuickSettings -> Classifier.QUICK_SETTINGS
else -> null
}
@@ -109,7 +110,7 @@ constructor(
val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
// Only enforce falsing if moving from the lockscreen scene to a new scene.
- val fromLockscreenScene = currentScene.value == SceneKey.Lockscreen
+ val fromLockscreenScene = currentScene.value == Scenes.Lockscreen
!fromLockscreenScene || !isFalseTouch
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index a4ba2a241275..8fe84c98866b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -42,11 +42,9 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
-import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CallbackController;
import dagger.Lazy;
@@ -71,18 +69,19 @@ public class RecordingController
private CountDownTimer mCountDownTimer = null;
private final Executor mMainExecutor;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final Context mContext;
private final FeatureFlags mFlags;
- private final UserContextProvider mUserContextProvider;
private final UserTracker mUserTracker;
private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
- private final SystemUIDialog.Factory mDialogFactory;
+ private final ScreenCaptureDisabledDialogDelegate mScreenCaptureDisabledDialogDelegate;
+ private final ScreenRecordDialogDelegate.Factory mScreenRecordDialogFactory;
+ private final ScreenRecordPermissionDialogDelegate.Factory
+ mScreenRecordPermissionDialogDelegateFactory;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
protected static final String EXTRA_STATE = "extra_state";
- private CopyOnWriteArrayList<RecordingStateChangeCallback> mListeners =
+ private final CopyOnWriteArrayList<RecordingStateChangeCallback> mListeners =
new CopyOnWriteArrayList<>();
private final Lazy<ScreenCaptureDevicePolicyResolver> mDevicePolicyResolver;
@@ -115,24 +114,26 @@ public class RecordingController
* Create a new RecordingController
*/
@Inject
- public RecordingController(@Main Executor mainExecutor,
+ public RecordingController(
+ @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher,
- Context context,
FeatureFlags flags,
- UserContextProvider userContextProvider,
Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
UserTracker userTracker,
MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
- SystemUIDialog.Factory dialogFactory) {
+ ScreenCaptureDisabledDialogDelegate screenCaptureDisabledDialogDelegate,
+ ScreenRecordDialogDelegate.Factory screenRecordDialogFactory,
+ ScreenRecordPermissionDialogDelegate.Factory
+ screenRecordPermissionDialogDelegateFactory) {
mMainExecutor = mainExecutor;
- mContext = context;
mFlags = flags;
mDevicePolicyResolver = devicePolicyResolver;
mBroadcastDispatcher = broadcastDispatcher;
- mUserContextProvider = userContextProvider;
mUserTracker = userTracker;
mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
- mDialogFactory = dialogFactory;
+ mScreenCaptureDisabledDialogDelegate = screenCaptureDisabledDialogDelegate;
+ mScreenRecordDialogFactory = screenRecordDialogFactory;
+ mScreenRecordPermissionDialogDelegateFactory = screenRecordPermissionDialogDelegateFactory;
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
@@ -163,27 +164,18 @@ public class RecordingController
if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)
&& mDevicePolicyResolver.get()
.isScreenCaptureCompletelyDisabled(getHostUserHandle())) {
- return new ScreenCaptureDisabledDialog(mContext);
+ return mScreenCaptureDisabledDialogDelegate.createDialog();
}
mMediaProjectionMetricsLogger.notifyProjectionInitiated(
getHostUid(), SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
- return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
- ? mDialogFactory.create(new ScreenRecordPermissionDialogDelegate(
- getHostUserHandle(),
- getHostUid(),
- /* controller= */ this,
- activityStarter,
- mUserContextProvider,
- onStartRecordingClicked,
- mMediaProjectionMetricsLogger,
- mDialogFactory))
- : new ScreenRecordDialog(
- context,
- /* controller= */ this,
- mUserContextProvider,
- onStartRecordingClicked);
+ return (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
+ ? mScreenRecordPermissionDialogDelegateFactory
+ .create(this, getHostUserHandle(), getHostUid(), onStartRecordingClicked)
+ : mScreenRecordDialogFactory
+ .create(this, onStartRecordingClicked))
+ .createDialog();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java
index b98093e50920..9f1447b1f509 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java
@@ -49,52 +49,69 @@ import com.android.systemui.res.R;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.Arrays;
import java.util.List;
/**
* Dialog to select screen recording options
*/
-public class ScreenRecordDialog extends SystemUIDialog {
+public class ScreenRecordDialogDelegate implements SystemUIDialog.Delegate {
private static final List<ScreenRecordingAudioSource> MODES = Arrays.asList(INTERNAL, MIC,
MIC_AND_INTERNAL);
private static final long DELAY_MS = 3000;
private static final long INTERVAL_MS = 1000;
- private final RecordingController mController;
+ private final SystemUIDialog.Factory mSystemUIDialogFactory;
private final UserContextProvider mUserContextProvider;
- @Nullable
+ private final RecordingController mController;
private final Runnable mOnStartRecordingClicked;
private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
- public ScreenRecordDialog(Context context,
- RecordingController controller,
- UserContextProvider userContextProvider,
- @Nullable Runnable onStartRecordingClicked) {
- super(context);
- mController = controller;
+ @AssistedFactory
+ public interface Factory {
+ ScreenRecordDialogDelegate create(
+ RecordingController recordingController,
+ @Nullable Runnable onStartRecordingClicked
+ );
+ }
+
+ @AssistedInject
+ public ScreenRecordDialogDelegate(
+ SystemUIDialog.Factory systemUIDialogFactory,
+ UserContextProvider userContextProvider,
+ @Assisted RecordingController controller,
+ @Assisted @Nullable Runnable onStartRecordingClicked) {
+ mSystemUIDialogFactory = systemUIDialogFactory;
mUserContextProvider = userContextProvider;
+ mController = controller;
mOnStartRecordingClicked = onStartRecordingClicked;
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ public SystemUIDialog createDialog() {
+ return mSystemUIDialogFactory.create(this);
+ }
- Window window = getWindow();
+ @Override
+ public void onCreate(SystemUIDialog dialog, Bundle savedInstanceState) {
+ Window window = dialog.getWindow();
window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setGravity(Gravity.CENTER);
- setTitle(R.string.screenrecord_title);
+ dialog.setTitle(R.string.screenrecord_title);
- setContentView(R.layout.screen_record_dialog);
+ dialog.setContentView(R.layout.screen_record_dialog);
- TextView cancelBtn = findViewById(R.id.button_cancel);
- cancelBtn.setOnClickListener(v -> dismiss());
- TextView startBtn = findViewById(R.id.button_start);
+ TextView cancelBtn = dialog.findViewById(R.id.button_cancel);
+ cancelBtn.setOnClickListener(v -> dialog.dismiss());
+ TextView startBtn = dialog.findViewById(R.id.button_start);
startBtn.setOnClickListener(v -> {
if (mOnStartRecordingClicked != null) {
// Note that it is important to run this callback before dismissing, so that the
@@ -104,13 +121,13 @@ public class ScreenRecordDialog extends SystemUIDialog {
// Start full-screen recording
requestScreenCapture(/* captureTarget= */ null);
- dismiss();
+ dialog.dismiss();
});
- mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
- mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
- mOptions = findViewById(R.id.screen_recording_options);
- ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(),
+ mAudioSwitch = dialog.findViewById(R.id.screenrecord_audio_switch);
+ mTapsSwitch = dialog.findViewById(R.id.screenrecord_taps_switch);
+ mOptions = dialog.findViewById(R.id.screen_recording_options);
+ ArrayAdapter a = new ScreenRecordingAdapter(dialog.getContext().getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item,
MODES);
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index 3eb26f498921..ba775cd3cd82 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -46,17 +46,20 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.SystemUIDialog
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
/** Dialog to select screen recording options */
-class ScreenRecordPermissionDialogDelegate(
- private val hostUserHandle: UserHandle,
- private val hostUid: Int,
- private val controller: RecordingController,
+class ScreenRecordPermissionDialogDelegate @AssistedInject constructor(
+ @Assisted private val hostUserHandle: UserHandle,
+ @Assisted private val hostUid: Int,
+ @Assisted private val controller: RecordingController,
private val activityStarter: ActivityStarter,
private val userContextProvider: UserContextProvider,
- private val onStartRecordingClicked: Runnable?,
+ @Assisted private val onStartRecordingClicked: Runnable?,
mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
- private val systemUIDialogFactory: SystemUIDialog.Factory
+ private val systemUIDialogFactory: SystemUIDialog.Factory,
) :
BaseMediaProjectionPermissionDialogDelegate<SystemUIDialog>(
createOptionList(),
@@ -65,8 +68,19 @@ class ScreenRecordPermissionDialogDelegate(
mediaProjectionMetricsLogger,
R.drawable.ic_screenrecord,
R.color.screenrecord_icon_color
- ),
- SystemUIDialog.Delegate {
+ ), SystemUIDialog.Delegate {
+
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ recordingController: RecordingController,
+ hostUserHandle: UserHandle,
+ hostUid: Int,
+ onStartRecordingClicked: Runnable?
+ ): ScreenRecordPermissionDialogDelegate
+ }
+
private lateinit var tapsSwitch: Switch
private lateinit var tapsSwitchContainer: ViewGroup
private lateinit var tapsView: View
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
new file mode 100644
index 000000000000..2294fc0be520
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.animation.Animator
+import android.app.Notification
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.view.Display
+import android.view.LayoutInflater
+import android.view.ScrollCaptureResponse
+import android.view.View
+import android.view.ViewTreeObserver
+import android.view.WindowInsets
+import android.window.OnBackInvokedDispatcher
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.res.R
+
+/**
+ * Legacy implementation of screenshot view methods. Just proxies the calls down into the original
+ * ScreenshotView.
+ */
+class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy {
+ override val view: ScreenshotView =
+ LayoutInflater.from(context).inflate(R.layout.screenshot, null) as ScreenshotView
+ override val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener
+ override val screenshotPreview: View
+
+ override var defaultDisplay: Int = Display.DEFAULT_DISPLAY
+ set(value) {
+ view.setDefaultDisplay(value)
+ }
+ override var defaultTimeoutMillis: Long = 6000
+ set(value) {
+ view.setDefaultTimeoutMillis(value)
+ }
+ override var onKeyListener: View.OnKeyListener? = null
+ set(value) {
+ view.setOnKeyListener(value)
+ }
+ override var flags: FeatureFlags? = null
+ set(value) {
+ view.setFlags(value)
+ }
+ override var packageName: String = ""
+ set(value) {
+ view.setPackageName(value)
+ }
+ override var logger: UiEventLogger? = null
+ set(value) {
+ view.setUiEventLogger(value)
+ }
+ override var callbacks: ScreenshotView.ScreenshotViewCallback? = null
+ set(value) {
+ view.setCallbacks(value)
+ }
+ override var screenshot: ScreenshotData? = null
+ set(value) {
+ view.setScreenshot(value)
+ }
+
+ override val isAttachedToWindow
+ get() = view.isAttachedToWindow
+ override val isDismissing
+ get() = view.isDismissing
+ override val isPendingSharedTransition
+ get() = view.isPendingSharedTransition
+
+ init {
+ internalInsetsListener = view
+ screenshotPreview = view.screenshotPreview
+ }
+
+ override fun reset() = view.reset()
+ override fun updateInsets(insets: WindowInsets) = view.updateInsets(insets)
+ override fun updateOrientation(insets: WindowInsets) = view.updateOrientation(insets)
+
+ override fun badgeScreenshot(userBadgedIcon: Drawable) = view.badgeScreenshot(userBadgedIcon)
+
+ override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator =
+ view.createScreenshotDropInAnimation(screenRect, showFlash)
+
+ override fun addQuickShareChip(quickShareAction: Notification.Action) =
+ view.addQuickShareChip(quickShareAction)
+
+ override fun setChipIntents(imageData: ScreenshotController.SavedImageData) =
+ view.setChipIntents(imageData)
+
+ override fun animateDismissal() = view.animateDismissal()
+
+ override fun showScrollChip(packageName: String, onClick: Runnable) =
+ view.showScrollChip(packageName, onClick)
+
+ override fun hideScrollChip() = view.hideScrollChip()
+
+ override fun prepareScrollingTransition(
+ response: ScrollCaptureResponse,
+ screenBitmap: Bitmap,
+ newScreenshot: Bitmap,
+ screenshotTakenInPortrait: Boolean
+ ) =
+ view.prepareScrollingTransition(
+ response,
+ screenBitmap,
+ newScreenshot,
+ screenshotTakenInPortrait
+ )
+
+ override fun startLongScreenshotTransition(
+ transitionDestination: Rect,
+ onTransitionEnd: Runnable,
+ longScreenshot: ScrollCaptureController.LongScreenshot
+ ) = view.startLongScreenshotTransition(transitionDestination, onTransitionEnd, longScreenshot)
+
+ override fun restoreNonScrollingUi() = view.restoreNonScrollingUi()
+
+ override fun stopInputListening() = view.stopInputListening()
+
+ override fun requestFocus() {
+ view.requestFocus()
+ }
+
+ override fun announceForAccessibility(string: String) = view.announceForAccessibility(string)
+
+ override fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener) =
+ view.addOnAttachStateChangeListener(listener)
+
+ override fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher? =
+ view.findOnBackInvokedDispatcher()
+
+ override fun getViewTreeObserver(): ViewTreeObserver = view.viewTreeObserver
+
+ override fun post(runnable: Runnable) {
+ view.post(runnable)
+ }
+
+ class Factory : ScreenshotViewProxy.Factory {
+ override fun getProxy(context: Context): ScreenshotViewProxy {
+ return LegacyScreenshotViewProxy(context)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index ee3e7ba9e8b3..13448d258a2c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -65,7 +65,6 @@ import android.view.Display;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
@@ -165,7 +164,7 @@ public class ScreenshotController {
/**
* Structure returned by the SaveImageInBackgroundTask
*/
- static class SavedImageData {
+ public static class SavedImageData {
public Uri uri;
public List<Notification.Action> smartActions;
public Notification.Action quickShareAction;
@@ -237,6 +236,7 @@ public class ScreenshotController {
private final WindowContext mContext;
private final FeatureFlags mFlags;
+ private final ScreenshotViewProxy mViewProxy;
private final ScreenshotNotificationsController mNotificationsController;
private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger;
@@ -272,7 +272,6 @@ public class ScreenshotController {
respondToKeyDismissal();
};
- private ScreenshotView mScreenshotView;
private final MessageContainerController mMessageContainerController;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
@@ -305,6 +304,7 @@ public class ScreenshotController {
ScreenshotController(
Context context,
FeatureFlags flags,
+ ScreenshotViewProxy.Factory viewProxyFactory,
ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
ScrollCaptureClient scrollCaptureClient,
@@ -360,6 +360,8 @@ public class ScreenshotController {
mMessageContainerController = messageContainerController;
mAssistContentRequester = assistContentRequester;
+ mViewProxy = viewProxyFactory.getProxy(mContext);
+
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
// Setup the window that we are going to use
@@ -461,7 +463,7 @@ public class ScreenshotController {
// The window is focusable by default
setWindowFocusable(true);
- mScreenshotView.requestFocus();
+ mViewProxy.requestFocus();
enqueueScrollCaptureRequest(screenshot.getUserHandle());
@@ -485,10 +487,10 @@ public class ScreenshotController {
mMessageContainerController.onScreenshotTaken(screenshot);
});
- mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
+ mViewProxy.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
mContext.getDrawable(R.drawable.overlay_badge_background),
screenshot.getUserHandle()));
- mScreenshotView.setScreenshot(screenshot);
+ mViewProxy.setScreenshot(screenshot);
// ignore system bar insets for the purpose of window layout
mWindow.getDecorView().setOnApplyWindowInsetsListener(
@@ -503,31 +505,31 @@ public class ScreenshotController {
void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
withWindowAttached(() -> {
if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
- mScreenshotView.announceForAccessibility(mContext.getResources().getString(
+ mViewProxy.announceForAccessibility(mContext.getResources().getString(
R.string.screenshot_saving_work_profile_title));
} else {
- mScreenshotView.announceForAccessibility(
+ mViewProxy.announceForAccessibility(
mContext.getResources().getString(R.string.screenshot_saving_title));
}
});
- mScreenshotView.reset();
+ mViewProxy.reset();
- if (mScreenshotView.isAttachedToWindow()) {
+ if (mViewProxy.isAttachedToWindow()) {
// if we didn't already dismiss for another reason
- if (!mScreenshotView.isDismissing()) {
+ if (!mViewProxy.isDismissing()) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
oldPackageName);
}
if (DEBUG_WINDOW) {
Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
- + "(dismissing=" + mScreenshotView.isDismissing() + ")");
+ + "(dismissing=" + mViewProxy.isDismissing() + ")");
}
}
- mScreenshotView.setPackageName(mPackageName);
+ mViewProxy.setPackageName(mPackageName);
- mScreenshotView.updateOrientation(
+ mViewProxy.updateOrientation(
mWindowManager.getCurrentWindowMetrics().getWindowInsets());
}
@@ -539,7 +541,7 @@ public class ScreenshotController {
Log.d(TAG, "dismissScreenshot");
}
// If we're already animating out, don't restart the animation
- if (mScreenshotView.isDismissing()) {
+ if (mViewProxy.isDismissing()) {
if (DEBUG_DISMISS) {
Log.v(TAG, "Already dismissing, ignoring duplicate command");
}
@@ -547,11 +549,11 @@ public class ScreenshotController {
}
mUiEventLogger.log(event, 0, mPackageName);
mScreenshotHandler.cancelTimeout();
- mScreenshotView.animateDismissal();
+ mViewProxy.animateDismissal();
}
boolean isPendingSharedTransition() {
- return mScreenshotView.isPendingSharedTransition();
+ return mViewProxy.isPendingSharedTransition();
}
// Any cleanup needed when the service is being destroyed.
@@ -576,7 +578,7 @@ public class ScreenshotController {
private void releaseMediaPlayer() {
if (mScreenshotSoundController == null) return;
- mScreenshotSoundController.releaseScreenshotSound();
+ mScreenshotSoundController.releaseScreenshotSoundAsync();
}
private void respondToKeyDismissal() {
@@ -591,18 +593,15 @@ public class ScreenshotController {
Log.d(TAG, "reloadAssets()");
}
- // Inflate the screenshot layout
- mScreenshotView = (ScreenshotView)
- LayoutInflater.from(mContext).inflate(R.layout.screenshot, null);
- mMessageContainerController.setView(mScreenshotView);
- mScreenshotView.addOnAttachStateChangeListener(
+ mMessageContainerController.setView(mViewProxy.getView());
+ mViewProxy.addOnAttachStateChangeListener(
new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(@NonNull View v) {
if (DEBUG_INPUT) {
Log.d(TAG, "Registering Predictive Back callback");
}
- mScreenshotView.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ mViewProxy.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
}
@@ -611,11 +610,12 @@ public class ScreenshotController {
if (DEBUG_INPUT) {
Log.d(TAG, "Unregistering Predictive Back callback");
}
- mScreenshotView.findOnBackInvokedDispatcher()
+ mViewProxy.findOnBackInvokedDispatcher()
.unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
}
});
- mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() {
+ mViewProxy.setLogger(mUiEventLogger);
+ mViewProxy.setCallbacks(new ScreenshotView.ScreenshotViewCallback() {
@Override
public void onUserInteraction() {
if (DEBUG_INPUT) {
@@ -640,11 +640,12 @@ public class ScreenshotController {
// TODO(159460485): Remove this when focus is handled properly in the system
setWindowFocusable(false);
}
- }, mFlags);
- mScreenshotView.setDefaultDisplay(mDisplayId);
- mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
+ });
+ mViewProxy.setFlags(mFlags);
+ mViewProxy.setDefaultDisplay(mDisplayId);
+ mViewProxy.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
- mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
+ mViewProxy.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
if (DEBUG_INPUT) {
Log.d(TAG, "onKeyEvent: " + keyCode);
@@ -658,23 +659,24 @@ public class ScreenshotController {
if (DEBUG_WINDOW) {
Log.d(TAG, "adding OnComputeInternalInsetsListener");
}
- mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
+ mViewProxy.getViewTreeObserver().addOnComputeInternalInsetsListener(
+ mViewProxy.getInternalInsetsListener());
if (DEBUG_WINDOW) {
- Log.d(TAG, "setContentView: " + mScreenshotView);
+ Log.d(TAG, "setContentView: " + mViewProxy.getView());
}
- setContentView(mScreenshotView);
+ setContentView(mViewProxy.getView());
}
private void prepareAnimation(Rect screenRect, boolean showFlash,
Runnable onAnimationComplete) {
- mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
+ mViewProxy.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (DEBUG_WINDOW) {
Log.d(TAG, "onPreDraw: startAnimation");
}
- mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
+ mViewProxy.getViewTreeObserver().removeOnPreDrawListener(this);
startAnimation(screenRect, showFlash, onAnimationComplete);
return true;
}
@@ -694,13 +696,13 @@ public class ScreenshotController {
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
// Hide the scroll chip until we know it's available in this
// orientation
- mScreenshotView.hideScrollChip();
+ mViewProxy.hideScrollChip();
// Delay scroll capture eval a bit to allow the underlying activity
// to set up in the new orientation.
mScreenshotHandler.postDelayed(() -> {
requestScrollCapture(owner);
}, 150);
- mScreenshotView.updateInsets(
+ mViewProxy.updateInsets(
mWindowManager.getCurrentWindowMetrics().getWindowInsets());
// Screenshot animation calculations won't be valid anymore,
// so just end
@@ -759,16 +761,16 @@ public class ScreenshotController {
+ mLastScrollCaptureResponse.getWindowTitle() + "]");
final ScrollCaptureResponse response = mLastScrollCaptureResponse;
- mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
+ mViewProxy.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
getDisplay().getRealMetrics(displayMetrics);
Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplayId,
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
- mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
+ mViewProxy.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
mScreenshotTakenInPortrait);
// delay starting scroll capture to make sure the scrim is up before the app moves
- mScreenshotView.post(() -> runBatchScrollCapture(response, owner));
+ mViewProxy.post(() -> runBatchScrollCapture(response, owner));
});
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "requestScrollCapture failed", e);
@@ -794,19 +796,19 @@ public class ScreenshotController {
return;
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "Exception", e);
- mScreenshotView.restoreNonScrollingUi();
+ mViewProxy.restoreNonScrollingUi();
return;
}
if (longScreenshot.getHeight() == 0) {
- mScreenshotView.restoreNonScrollingUi();
+ mViewProxy.restoreNonScrollingUi();
return;
}
mLongScreenshotHolder.setLongScreenshot(longScreenshot);
mLongScreenshotHolder.setTransitionDestinationCallback(
(transitionDestination, onTransitionEnd) -> {
- mScreenshotView.startLongScreenshotTransition(
+ mViewProxy.startLongScreenshotTransition(
transitionDestination, onTransitionEnd,
longScreenshot);
// TODO: Do this via ActionIntentExecutor instead.
@@ -882,16 +884,14 @@ public class ScreenshotController {
}
mWindowManager.removeViewImmediate(decorView);
}
- // Ensure that we remove the input monitor
- if (mScreenshotView != null) {
- mScreenshotView.stopInputListening();
- }
+
+ mViewProxy.stopInputListening();
}
private void playCameraSoundIfNeeded() {
if (mScreenshotSoundController == null) return;
// the controller is not-null only on the default display controller
- mScreenshotSoundController.playCameraSound();
+ mScreenshotSoundController.playScreenshotSoundAsync();
}
/**
@@ -932,7 +932,7 @@ public class ScreenshotController {
}
mScreenshotAnimation =
- mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
+ mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash);
if (onAnimationComplete != null) {
mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -975,7 +975,7 @@ public class ScreenshotController {
};
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null,
- Pair.create(mScreenshotView.getScreenshotPreview(),
+ Pair.create(mViewProxy.getScreenshotPreview(),
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
return transition;
@@ -999,7 +999,7 @@ public class ScreenshotController {
mCurrentRequestCallback.onFinish();
mCurrentRequestCallback = null;
}
- mScreenshotView.reset();
+ mViewProxy.reset();
removeWindow();
mScreenshotHandler.cancelTimeout();
}
@@ -1067,7 +1067,7 @@ public class ScreenshotController {
}
private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
- mScreenshotView.setChipIntents(imageData);
+ mViewProxy.setChipIntents(imageData);
}
/**
@@ -1084,11 +1084,11 @@ public class ScreenshotController {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
+ mViewProxy.addQuickShareChip(quickShareData.quickShareAction);
}
});
} else {
- mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
+ mViewProxy.addQuickShareChip(quickShareData.quickShareAction);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 2c0bddecc58e..d3a7fc4a3e4a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -21,22 +21,34 @@ import android.util.Log
import com.android.app.tracing.coroutines.async
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.google.errorprone.annotations.CanIgnoreReturnValue
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
/** Controls sound reproduction after a screenshot is taken. */
interface ScreenshotSoundController {
/** Reproduces the camera sound. */
- @CanIgnoreReturnValue fun playCameraSound(): Deferred<Unit>
+ suspend fun playScreenshotSound()
- /** Releases the sound. [playCameraSound] behaviour is undefined after this has been called. */
- @CanIgnoreReturnValue fun releaseScreenshotSound(): Deferred<Unit>
+ /**
+ * Releases the sound. [playScreenshotSound] behaviour is undefined after this has been called.
+ */
+ suspend fun releaseScreenshotSound()
+
+ /** Reproduces the camera sound. Used for compatibility with Java code. */
+ fun playScreenshotSoundAsync()
+
+ /**
+ * Releases the sound. [playScreenshotSound] behaviour is undefined after this has been called.
+ * Used for compatibility with Java code.
+ */
+ fun releaseScreenshotSoundAsync()
}
class ScreenshotSoundControllerImpl
@@ -47,8 +59,8 @@ constructor(
@Background private val bgDispatcher: CoroutineDispatcher
) : ScreenshotSoundController {
- val player: Deferred<MediaPlayer?> =
- coroutineScope.async("loadCameraSound", bgDispatcher) {
+ private val player: Deferred<MediaPlayer?> =
+ coroutineScope.async("loadScreenshotSound", bgDispatcher) {
try {
soundProvider.getScreenshotSound()
} catch (e: IllegalStateException) {
@@ -57,8 +69,8 @@ constructor(
}
}
- override fun playCameraSound(): Deferred<Unit> {
- return coroutineScope.async("playCameraSound", bgDispatcher) {
+ override suspend fun playScreenshotSound() {
+ withContext(bgDispatcher) {
try {
player.await()?.start()
} catch (e: IllegalStateException) {
@@ -68,8 +80,8 @@ constructor(
}
}
- override fun releaseScreenshotSound(): Deferred<Unit> {
- return coroutineScope.async("releaseScreenshotSound", bgDispatcher) {
+ override suspend fun releaseScreenshotSound() {
+ withContext(bgDispatcher) {
try {
withTimeout(1.seconds) { player.await()?.release() }
} catch (e: TimeoutCancellationException) {
@@ -79,6 +91,14 @@ constructor(
}
}
+ override fun playScreenshotSoundAsync() {
+ coroutineScope.launch { playScreenshotSound() }
+ }
+
+ override fun releaseScreenshotSoundAsync() {
+ coroutineScope.launch { releaseScreenshotSound() }
+ }
+
private companion object {
const val TAG = "ScreenshotSoundControllerImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index be30a1576b21..8a8766dbab94 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -102,7 +102,7 @@ import java.util.ArrayList;
public class ScreenshotView extends FrameLayout implements
ViewTreeObserver.OnComputeInternalInsetsListener {
- interface ScreenshotViewCallback {
+ public interface ScreenshotViewCallback {
void onUserInteraction();
void onAction(Intent intent, UserHandle owner, boolean overrideTransition);
@@ -426,15 +426,15 @@ public class ScreenshotView extends FrameLayout implements
return mScreenshotPreview;
}
- /**
- * Set up the logger and callback on dismissal.
- *
- * Note: must be called before any other (non-constructor) method or null pointer exceptions
- * may occur.
- */
- void init(UiEventLogger uiEventLogger, ScreenshotViewCallback callbacks, FeatureFlags flags) {
+ void setUiEventLogger(UiEventLogger uiEventLogger) {
mUiEventLogger = uiEventLogger;
+ }
+
+ void setCallbacks(ScreenshotViewCallback callbacks) {
mCallbacks = callbacks;
+ }
+
+ void setFlags(FeatureFlags flags) {
mFlags = flags;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
new file mode 100644
index 000000000000..0064521bd3a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.animation.Animator
+import android.app.Notification
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.view.ScrollCaptureResponse
+import android.view.View
+import android.view.View.OnKeyListener
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.view.WindowInsets
+import android.window.OnBackInvokedDispatcher
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.flags.FeatureFlags
+
+/** Abstraction of the surface between ScreenshotController and ScreenshotView */
+interface ScreenshotViewProxy {
+ val view: ViewGroup
+ val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener
+ val screenshotPreview: View
+
+ var defaultDisplay: Int
+ var defaultTimeoutMillis: Long
+ var onKeyListener: OnKeyListener?
+ var flags: FeatureFlags?
+ var packageName: String
+ var logger: UiEventLogger?
+ var callbacks: ScreenshotView.ScreenshotViewCallback?
+ var screenshot: ScreenshotData?
+
+ val isAttachedToWindow: Boolean
+ val isDismissing: Boolean
+ val isPendingSharedTransition: Boolean
+
+ fun reset()
+ fun updateInsets(insets: WindowInsets)
+ fun updateOrientation(insets: WindowInsets)
+ fun badgeScreenshot(userBadgedIcon: Drawable)
+ fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator
+ fun addQuickShareChip(quickShareAction: Notification.Action)
+ fun setChipIntents(imageData: ScreenshotController.SavedImageData)
+ fun animateDismissal()
+
+ fun showScrollChip(packageName: String, onClick: Runnable)
+ fun hideScrollChip()
+ fun prepareScrollingTransition(
+ response: ScrollCaptureResponse,
+ screenBitmap: Bitmap,
+ newScreenshot: Bitmap,
+ screenshotTakenInPortrait: Boolean
+ )
+ fun startLongScreenshotTransition(
+ transitionDestination: Rect,
+ onTransitionEnd: Runnable,
+ longScreenshot: ScrollCaptureController.LongScreenshot
+ )
+ fun restoreNonScrollingUi()
+
+ fun stopInputListening()
+ fun requestFocus()
+ fun announceForAccessibility(string: String)
+ fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener)
+ fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher?
+ fun getViewTreeObserver(): ViewTreeObserver
+ fun post(runnable: Runnable)
+
+ interface Factory {
+ fun getProxy(context: Context): ScreenshotViewProxy
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index bb34ede2cf5e..8a2678c8ab09 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -75,7 +75,7 @@ public class ScrollCaptureController {
private String mWindowOwner;
private volatile boolean mCancelled;
- static class LongScreenshot {
+ public static class LongScreenshot {
private final ImageTileSet mImageTileSet;
private final Session mSession;
// TODO: Add UserHandle so LongScreenshots can adhere to work profile screenshot policy
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 3797b8b41e5a..a00c81d43b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -20,6 +20,7 @@ import android.app.Service;
import com.android.systemui.screenshot.ImageCapture;
import com.android.systemui.screenshot.ImageCaptureImpl;
+import com.android.systemui.screenshot.LegacyScreenshotViewProxy;
import com.android.systemui.screenshot.RequestProcessor;
import com.android.systemui.screenshot.ScreenshotPolicy;
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
@@ -29,12 +30,14 @@ import com.android.systemui.screenshot.ScreenshotSoundController;
import com.android.systemui.screenshot.ScreenshotSoundControllerImpl;
import com.android.systemui.screenshot.ScreenshotSoundProvider;
import com.android.systemui.screenshot.ScreenshotSoundProviderImpl;
+import com.android.systemui.screenshot.ScreenshotViewProxy;
import com.android.systemui.screenshot.TakeScreenshotService;
import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
import com.android.systemui.screenshot.appclips.AppClipsService;
import dagger.Binds;
import dagger.Module;
+import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
@@ -81,4 +84,9 @@ public abstract class ScreenshotModule {
@Binds
abstract ScreenshotSoundController bindScreenshotSoundController(
ScreenshotSoundControllerImpl screenshotSoundProviderImpl);
+
+ @Provides
+ static ScreenshotViewProxy.Factory providesScreenshotViewProxyFactory() {
+ return new LegacyScreenshotViewProxy.Factory();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 6af9b739da52..e92630fc67a2 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -55,15 +55,15 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.settings.SecureSettings;
-import java.util.concurrent.Executor;
-
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import java.util.concurrent.Executor;
+
public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
private static final String TAG = "CentralSurfaces.BrightnessController";
- private static final int SLIDER_ANIMATION_DURATION = 3000;
+ private static final int SLIDER_ANIMATION_DURATION = 1000;
private static final int MSG_UPDATE_SLIDER = 1;
private static final int MSG_ATTACH_LISTENER = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index df845f559f2e..d3869baf16a2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -23,11 +23,12 @@ import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.compose.ComposeFacade.createCommunalContainer
-import com.android.systemui.compose.ComposeFacade.isComposeAvailable
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.res.R
@@ -35,7 +36,6 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.kotlin.collectFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
/**
* Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -107,8 +107,7 @@ constructor(
private var shadeShowing = false
/** Returns a flow that tracks whether communal hub is available. */
- fun communalAvailable(): Flow<Boolean> =
- if (isComposeAvailable()) communalInteractor.isCommunalAvailable else flowOf(false)
+ fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable
/**
* Creates the container view containing the glanceable hub UI.
@@ -118,7 +117,11 @@ constructor(
fun initView(
context: Context,
): View {
- return initView(createCommunalContainer(context, communalViewModel))
+ return initView(
+ ComposeView(context).apply {
+ setContent { PlatformTheme { CommunalContainer(viewModel = communalViewModel) } }
+ }
+ )
}
/** Override for testing. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7b330b0f3803..2fd438be9610 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -111,8 +111,6 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.animation.ActivityTransitionAnimator;
-import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
@@ -219,7 +217,6 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
@@ -259,10 +256,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private static final boolean DEBUG_DRAWABLE = false;
/** The parallax amount of the quick settings translation when dragging down the panel. */
public static final float QS_PARALLAX_AMOUNT = 0.175f;
- private static final long ANIMATION_DELAY_ICON_FADE_IN =
- ActivityTransitionAnimator.TIMINGS.getTotalDuration()
- - CollapsedStatusBarFragment.FADE_IN_DURATION
- - CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
private static final int NO_FIXED_DURATION = -1;
private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
@@ -358,7 +351,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private final AccessibilityDelegate mAccessibilityDelegate = new ShadeAccessibilityDelegate();
private final NotificationGutsManager mGutsManager;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
- private final QuickSettingsController mQsController;
+ private final QuickSettingsControllerImpl mQsController;
private final NaturalScrollingSettingObserver mNaturalScrollingSettingObserver;
private final TouchHandler mTouchHandler = new TouchHandler();
@@ -463,7 +456,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
*/
private float mLinearDarkAmount;
private boolean mPulsing;
- private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
/** Non-null if a heads-up notification's position is being tracked. */
@Nullable
@@ -734,7 +726,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
TapAgainViewController tapAgainViewController,
NavigationModeController navigationModeController,
NavigationBarController navigationBarController,
- QuickSettingsController quickSettingsController,
+ QuickSettingsControllerImpl quickSettingsController,
FragmentService fragmentService,
IStatusBarService statusBarService,
ContentResolver contentResolver,
@@ -2053,7 +2045,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mView.animate().cancel();
}
- @Override
public void expandToQs() {
if (mQsController.isExpansionEnabled()) {
mQsController.setExpandImmediate(true);
@@ -2820,7 +2811,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mQsController.setListening(listening);
}
- @Override
public void expand(boolean animate) {
if (isFullyCollapsed() || isCollapsing()) {
mInstantExpanding = true;
@@ -3074,7 +3064,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void onClosingFinished() {
- mOpenCloseListener.onClosingFinished();
+ if (mOpenCloseListener != null) {
+ mOpenCloseListener.onClosingFinished();
+ }
setClosingWithAlphaFadeout(false);
mMediaHierarchyManager.closeGuts();
}
@@ -3157,10 +3149,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
- @Override
public boolean shouldHideStatusBarIconsWhenExpanded() {
if (isLaunchingActivity()) {
- return mHideIconsDuringLaunchAnimation;
+ return false;
}
if (mHeadsUpAppearanceController != null
&& mHeadsUpAppearanceController.shouldBeVisible()) {
@@ -3258,18 +3249,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
@Override
- public void applyLaunchAnimationProgress(float linearProgress) {
- boolean hideIcons = TransitionAnimator.getProgress(ActivityTransitionAnimator.TIMINGS,
- linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
- if (hideIcons != mHideIconsDuringLaunchAnimation) {
- mHideIconsDuringLaunchAnimation = hideIcons;
- if (!hideIcons) {
- mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
- }
- }
- }
-
- @Override
public void performHapticFeedback(int constant) {
mVibratorHelper.performHapticFeedback(mView, constant);
}
@@ -3471,7 +3450,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount);
ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount);
ipw.print("mPulsing="); ipw.println(mPulsing);
- ipw.print("mHideIconsDuringLaunchAnimation="); ipw.println(mHideIconsDuringLaunchAnimation);
ipw.print("mStackScrollerMeasuringPass="); ipw.println(mStackScrollerMeasuringPass);
ipw.print("mPanelAlpha="); ipw.println(mPanelAlpha);
ipw.print("mBottomAreaShadeAlpha="); ipw.println(mBottomAreaShadeAlpha);
@@ -4163,7 +4141,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return mShadeRepository.getLegacyIsClosing().getValue();
}
- @Override
public void collapseWithDuration(int animationDuration) {
mFixedDuration = animationDuration;
collapse(false /* delayed */, 1.0f /* speedUpFactor */);
@@ -4705,7 +4682,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
if (mSplitShadeEnabled && !isKeyguardShowing()) {
mQsController.setExpandImmediate(true);
}
- mOpenCloseListener.onOpenStarted();
+ if (mOpenCloseListener != null) {
+ mOpenCloseListener.onOpenStarted();
+ }
}
if (state == STATE_CLOSED) {
mQsController.setExpandImmediate(false);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 8f9cef37dac7..f7fed537a167 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -60,7 +60,6 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
@@ -507,7 +506,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private void applyFitsSystemWindows(NotificationShadeWindowState state) {
boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
- if (!SceneContainerFlag.isEnabled() && mWindowRootView != null
+ if (mWindowRootView != null
&& mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) {
mWindowRootView.setFitsSystemWindows(fitsSystemWindows);
mWindowRootView.requestApplyInsets();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.kt b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.kt
new file mode 100644
index 000000000000..c96a339b560e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+interface QuickSettingsController {
+ /** Returns whether or not QuickSettings is expanded. */
+ val expanded: Boolean
+
+ /** Returns whether or not QuickSettings is being customized. */
+ val isCustomizing: Boolean
+
+ /** Returns Whether we should intercept a gesture to open Quick Settings. */
+ @Deprecated("specific to legacy touch handling")
+ fun shouldQuickSettingsIntercept(x: Float, y: Float, yDiff: Float): Boolean
+
+ /** Closes the Qs customizer. */
+ fun closeQsCustomizer()
+
+ /**
+ * This method closes QS but in split shade it should be used only in special cases: to make
+ * sure QS closes when shade is closed as well. Otherwise it will result in QS disappearing from
+ * split shade
+ */
+ @Deprecated("specific to legacy split shade") fun closeQs()
+
+ /** Calculate top padding for notifications */
+ @Deprecated("specific to legacy DebugDrawable")
+ fun calculateNotificationsTopPadding(
+ isShadeExpanding: Boolean,
+ keyguardNotificationStaticPadding: Int,
+ expandedFraction: Float,
+ ): Float
+
+ /** Calculate height of QS panel */
+ @Deprecated("specific to legacy DebugDrawable")
+ fun calculatePanelHeightExpanded(stackScrollerPadding: Int): Int
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index a5c055350999..19d98a0bb83c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -118,7 +118,7 @@ import javax.inject.Inject;
* TODO (b/264460656) make this dumpable
*/
@SysUISingleton
-public class QuickSettingsController implements Dumpable {
+public class QuickSettingsControllerImpl implements QuickSettingsController, Dumpable {
public static final String TAG = "QuickSettingsController";
public static final int SHADE_BACK_ANIM_SCALE_MULTIPLIER = 100;
@@ -252,7 +252,7 @@ public class QuickSettingsController implements Dumpable {
/**
* The window width currently in effect -- used together with
- * {@link QuickSettingsController#mCachedGestureInsets} to decide whether a back gesture should
+ * {@link QuickSettingsControllerImpl#mCachedGestureInsets} to decide whether a back gesture should
* receive a horizontal swipe inwards from the left/right vertical edge of the screen.
* We cache this on ACTION_DOWN, and query it during both ACTION_DOWN and ACTION_MOVE events.
*/
@@ -304,7 +304,7 @@ public class QuickSettingsController implements Dumpable {
private final QS.ScrollListener mQsScrollListener = this::onScroll;
@Inject
- public QuickSettingsController(
+ public QuickSettingsControllerImpl(
Lazy<NotificationPanelViewController> panelViewControllerLazy,
NotificationPanelView panelView,
QsFrameTranslateController qsFrameTranslateController,
@@ -399,23 +399,23 @@ public class QuickSettingsController implements Dumpable {
mQs = qs;
}
- public void setExpansionHeightListener(ExpansionHeightListener listener) {
+ void setExpansionHeightListener(ExpansionHeightListener listener) {
mExpansionHeightListener = listener;
}
- public void setQsStateUpdateListener(QsStateUpdateListener listener) {
+ void setQsStateUpdateListener(QsStateUpdateListener listener) {
mQsStateUpdateListener = listener;
}
- public void setApplyClippingImmediatelyListener(ApplyClippingImmediatelyListener listener) {
+ void setApplyClippingImmediatelyListener(ApplyClippingImmediatelyListener listener) {
mApplyClippingImmediatelyListener = listener;
}
- public void setFlingQsWithoutClickListener(FlingQsWithoutClickListener listener) {
+ void setFlingQsWithoutClickListener(FlingQsWithoutClickListener listener) {
mFlingQsWithoutClickListener = listener;
}
- public void setExpansionHeightSetToMaxListener(ExpansionHeightSetToMaxListener callback) {
+ void setExpansionHeightSetToMaxListener(ExpansionHeightSetToMaxListener callback) {
mExpansionHeightSetToMaxListener = callback;
}
@@ -507,15 +507,11 @@ public class QuickSettingsController implements Dumpable {
&& mPanelView.getRootWindowInsets().isVisible(ime());
}
- public boolean isExpansionEnabled() {
+ boolean isExpansionEnabled() {
return mExpansionEnabledPolicy && mExpansionEnabledAmbient
&& !isRemoteInputActiveWithKeyboardUp();
}
- public float getTransitioningToFullShadeProgress() {
- return mTransitioningToFullShadeProgress;
- }
-
/** */
@VisibleForTesting
boolean isExpandImmediate() {
@@ -536,7 +532,7 @@ public class QuickSettingsController implements Dumpable {
* Computes (and caches) the gesture insets for the current window. Intended to be called
* on ACTION_DOWN, and safely queried repeatedly thereafter during ACTION_MOVE events.
*/
- public void updateGestureInsetsCache() {
+ void updateGestureInsetsCache() {
WindowManager wm = this.mPanelView.getContext().getSystemService(WindowManager.class);
WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets(
@@ -548,7 +544,7 @@ public class QuickSettingsController implements Dumpable {
* Returns whether x coordinate lies in the vertical edges of the screen
* (the only place where a back gesture can be initiated).
*/
- public boolean shouldBackBypassQuickSettings(float touchX) {
+ boolean shouldBackBypassQuickSettings(float touchX) {
return (touchX < mCachedGestureInsets.left)
|| (touchX > mCachedWindowWidth - mCachedGestureInsets.right);
}
@@ -592,6 +588,7 @@ public class QuickSettingsController implements Dumpable {
return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
}
+ @Override
public boolean getExpanded() {
return mShadeRepository.getLegacyIsQsExpanded().getValue();
}
@@ -601,7 +598,7 @@ public class QuickSettingsController implements Dumpable {
return mShadeRepository.getLegacyQsTracking().getValue();
}
- public boolean getFullyExpanded() {
+ boolean getFullyExpanded() {
return mFullyExpanded;
}
@@ -623,27 +620,28 @@ public class QuickSettingsController implements Dumpable {
return mQs != null;
}
+ @Override
public boolean isCustomizing() {
return isQsFragmentCreated() && mQs.isCustomizing();
}
- public float getExpansionHeight() {
+ float getExpansionHeight() {
return mExpansionHeight;
}
- public boolean getExpandedWhenExpandingStarted() {
+ boolean getExpandedWhenExpandingStarted() {
return mExpandedWhenExpandingStarted;
}
- public int getMinExpansionHeight() {
+ int getMinExpansionHeight() {
return mMinExpansionHeight;
}
- public boolean isFullyExpandedAndTouchesDisallowed() {
+ boolean isFullyExpandedAndTouchesDisallowed() {
return isQsFragmentCreated() && getFullyExpanded() && disallowTouches();
}
- public int getMaxExpansionHeight() {
+ int getMaxExpansionHeight() {
return mMaxExpansionHeight;
}
@@ -654,13 +652,14 @@ public class QuickSettingsController implements Dumpable {
return !mTouchAboveFalsingThreshold;
}
- public int getFalsingThreshold() {
+ int getFalsingThreshold() {
return mFalsingThreshold;
}
/**
* Returns Whether we should intercept a gesture to open Quick Settings.
*/
+ @Override
public boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
boolean keyguardShowing = mBarState == KEYGUARD;
if (!isExpansionEnabled() || mCollapsedOnDown || (keyguardShowing
@@ -717,7 +716,7 @@ public class QuickSettingsController implements Dumpable {
* @param downY the y location where the touch started
* Returns true if the panel could be collapsed because it stared on QQS
*/
- public boolean canPanelCollapseOnQQS(float downX, float downY) {
+ boolean canPanelCollapseOnQQS(float downX, float downY) {
if (mCollapsedOnDown || mBarState == KEYGUARD || getExpanded()) {
return false;
}
@@ -727,6 +726,7 @@ public class QuickSettingsController implements Dumpable {
}
/** Closes the Qs customizer. */
+ @Override
public void closeQsCustomizer() {
if (mQs != null) {
mQs.closeCustomizer();
@@ -734,7 +734,7 @@ public class QuickSettingsController implements Dumpable {
}
/** Returns whether touches from the notification panel should be disallowed */
- public boolean disallowTouches() {
+ boolean disallowTouches() {
if (mQs != null) {
return mQs.disallowPanelTouches();
} else {
@@ -754,15 +754,11 @@ public class QuickSettingsController implements Dumpable {
}
}
- public void setDozing(boolean dozing) {
+ void setDozing(boolean dozing) {
mDozing = dozing;
}
- /**
- * This method closes QS but in split shade it should be used only in special cases: to make
- * sure QS closes when shade is closed as well. Otherwise it will result in QS disappearing
- * from split shade
- */
+ @Override
public void closeQs() {
if (mSplitShadeEnabled) {
mShadeLog.d("Closing QS while in split shade");
@@ -794,7 +790,7 @@ public class QuickSettingsController implements Dumpable {
}
/** update Qs height state */
- public void setExpansionHeight(float height) {
+ void setExpansionHeight(float height) {
int maxHeight = getMaxExpansionHeight();
height = Math.min(Math.max(
height, getMinExpansionHeight()), maxHeight);
@@ -817,7 +813,7 @@ public class QuickSettingsController implements Dumpable {
}
/** */
- public void setHeightOverrideToDesiredHeight() {
+ void setHeightOverrideToDesiredHeight() {
if (isSizeChangeAnimationRunning() && isQsFragmentCreated()) {
mQs.setHeightOverride(mQs.getDesiredHeight());
}
@@ -919,7 +915,7 @@ public class QuickSettingsController implements Dumpable {
}
/** Sets Qs ScrimEnabled and updates QS state. */
- public void setScrimEnabled(boolean scrimEnabled) {
+ void setScrimEnabled(boolean scrimEnabled) {
boolean changed = mScrimEnabled != scrimEnabled;
mScrimEnabled = scrimEnabled;
if (changed) {
@@ -995,7 +991,7 @@ public class QuickSettingsController implements Dumpable {
}
/** update expanded state of QS */
- public void updateExpansion() {
+ void updateExpansion() {
if (mQs == null) return;
final float squishiness;
if ((isExpandImmediate() || getExpanded()) && !mSplitShadeEnabled) {
@@ -1053,13 +1049,13 @@ public class QuickSettingsController implements Dumpable {
// mTransitioningToFullShadeProgress > 0 means we're doing regular lockscreen to shade
// transition. If that's not the case we should follow QS expansion fraction for when
// user is pulling from the same top to go directly to expanded QS
- return getTransitioningToFullShadeProgress() > 0
+ return mTransitioningToFullShadeProgress > 0
? mLockscreenShadeTransitionController.getQSDragProgress()
: computeExpansionFraction();
}
/** */
- public void updateExpansionEnabledAmbient() {
+ void updateExpansionEnabledAmbient() {
final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsHeaderHeight;
mExpansionEnabledAmbient = mSplitShadeEnabled
|| (mAmbientState.getScrollY() <= scrollRangeToTop);
@@ -1081,7 +1077,7 @@ public class QuickSettingsController implements Dumpable {
}
/** Calculate fraction of current QS expansion state */
- public float computeExpansionFraction() {
+ float computeExpansionFraction() {
if (mAnimatingHiddenFromCollapsed) {
// When hiding QS from collapsed state, the expansion can sometimes temporarily
// be larger than 0 because of the timing, leading to flickers.
@@ -1112,7 +1108,7 @@ public class QuickSettingsController implements Dumpable {
}
/** Called when shade starts expanding. */
- public void onExpandingStarted(boolean qsFullyExpanded) {
+ void onExpandingStarted(boolean qsFullyExpanded) {
mNotificationStackScrollLayoutController.onExpansionStarted();
mExpandedWhenExpandingStarted = qsFullyExpanded;
mMediaHierarchyManager.setCollapsingShadeFromQS(mExpandedWhenExpandingStarted
@@ -1363,7 +1359,7 @@ public class QuickSettingsController implements Dumpable {
}
}
- /** Calculate top padding for notifications */
+ @Override
public float calculateNotificationsTopPadding(boolean isShadeExpanding,
int keyguardNotificationStaticPadding, float expandedFraction) {
float topPadding;
@@ -1404,7 +1400,7 @@ public class QuickSettingsController implements Dumpable {
}
}
- /** Calculate height of QS panel */
+ @Override
public int calculatePanelHeightExpanded(int stackScrollerPadding) {
float
notificationHeight =
@@ -1576,7 +1572,6 @@ public class QuickSettingsController implements Dumpable {
}
}
- @VisibleForTesting
boolean isTrackingBlocked() {
return mConflictingExpansionGesture && getExpanded();
}
@@ -1591,7 +1586,7 @@ public class QuickSettingsController implements Dumpable {
}
/** handles touches in Qs panel area */
- public boolean handleTouch(MotionEvent event, boolean isFullyCollapsed,
+ boolean handleTouch(MotionEvent event, boolean isFullyCollapsed,
boolean isShadeOrQsHeightAnimationRunning) {
if (isSplitShadeAndTouchXOutsideQs(event.getX())) {
return false;
@@ -1747,7 +1742,7 @@ public class QuickSettingsController implements Dumpable {
}
/** intercepts touches on Qs panel area. */
- public boolean onIntercept(MotionEvent event) {
+ boolean onIntercept(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
@@ -1858,7 +1853,7 @@ public class QuickSettingsController implements Dumpable {
*
* @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
*/
- public void animateCloseQs(boolean animateAway) {
+ void animateCloseQs(boolean animateAway) {
if (mExpansionAnimator != null) {
if (!mAnimatorExpand) {
return;
@@ -1877,7 +1872,7 @@ public class QuickSettingsController implements Dumpable {
}
/** @see #flingQs(float, int, Runnable, boolean) */
- public void flingQs(float vel, int type) {
+ void flingQs(float vel, int type) {
flingQs(vel, type, null /* onFinishRunnable */, false /* isClick */);
}
@@ -2144,7 +2139,7 @@ public class QuickSettingsController implements Dumpable {
}
/** */
- public FragmentHostManager.FragmentListener getQsFragmentListener() {
+ FragmentHostManager.FragmentListener getQsFragmentListener() {
return new QsFragmentListener();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt
new file mode 100644
index 000000000000..b8250cc284bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerSceneImpl.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import javax.inject.Inject
+
+@SysUISingleton
+class QuickSettingsControllerSceneImpl
+@Inject
+constructor(
+ private val shadeInteractor: ShadeInteractor,
+ private val qsSceneAdapter: QSSceneAdapter,
+ private val qsContainerController: QSContainerController,
+) : QuickSettingsController {
+
+ override val expanded: Boolean
+ get() = shadeInteractor.isQsExpanded.value
+
+ override val isCustomizing: Boolean
+ get() = qsSceneAdapter.isCustomizing.value
+
+ @Deprecated("specific to legacy touch handling")
+ override fun shouldQuickSettingsIntercept(x: Float, y: Float, yDiff: Float): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun closeQsCustomizer() {
+ qsContainerController.setCustomizerShowing(false)
+ }
+
+ @Deprecated("specific to legacy split shade")
+ override fun closeQs() {
+ // Do nothing
+ }
+
+ @Deprecated("specific to legacy DebugDrawable")
+ override fun calculateNotificationsTopPadding(
+ isShadeExpanding: Boolean,
+ keyguardNotificationStaticPadding: Int,
+ expandedFraction: Float
+ ): Float {
+ throw UnsupportedOperationException()
+ }
+
+ @Deprecated("specific to legacy DebugDrawable")
+ override fun calculatePanelHeightExpanded(stackScrollerPadding: Int): Int {
+ throw UnsupportedOperationException()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index ec4b23a56483..0a57b64b1ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -78,6 +78,14 @@ public interface ShadeController extends CoreStartable {
*/
void animateCollapseShade(int flags, boolean force, boolean delayed, float speedUpFactor);
+ /**
+ * Collapses the shade with an animation duration in milliseconds.
+ *
+ * @deprecated use animateCollapseShade with a speed up factor instead
+ */
+ @Deprecated
+ void collapseWithDuration(int animationDuration);
+
/** Expand the shade with an animation. */
void animateExpandShade();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt
index 08a0c934702d..093690ffb881 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt
@@ -33,6 +33,7 @@ open class ShadeControllerEmptyImpl @Inject constructor() : ShadeController {
delayed: Boolean,
speedUpFactor: Float
) {}
+ override fun collapseWithDuration(animationDuration: Int) {}
override fun animateExpandShade() {}
override fun animateExpandQs() {}
override fun postAnimateCollapseShade() {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index e6555f2e0993..d99d607879cc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -147,6 +147,11 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
}
@Override
+ public void collapseWithDuration(int animationDuration) {
+ mNpvc.get().collapseWithDuration(animationDuration);
+ }
+
+ @Override
protected void expandToNotifications() {
getNpvc().expandToNotifications();
}
@@ -221,7 +226,6 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
}
}
-
@Override
public void collapseShade(boolean animate) {
if (animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 6a2a6a417f5a..27168a799086 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade
import android.view.MotionEvent
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.assist.AssistManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -25,7 +26,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.dagger.ShadeTouchLog
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys.CollapseShadeInstantly
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
@@ -96,7 +97,7 @@ constructor(
}
override fun instantCollapseShade() {
- // TODO(b/315921512) add support for instant transition
+ // TODO(b/325602936) add support for instant transition
sceneInteractor.changeScene(
getCollapseDestinationScene(),
"hide shade",
@@ -121,7 +122,7 @@ constructor(
// release focus immediately to kick off focus change transition
notificationShadeWindowController.setNotificationShadeFocusable(false)
notificationStackScrollLayout.cancelExpandHelper()
- sceneInteractor.changeScene(SceneKey.Shade, "ShadeController.animateExpandShade")
+ sceneInteractor.changeScene(Scenes.Shade, "ShadeController.animateExpandShade")
if (delayed) {
scope.launch {
delay(125)
@@ -133,6 +134,11 @@ constructor(
}
}
+ override fun collapseWithDuration(animationDuration: Int) {
+ // TODO(b/300258424) inline this. The only caller uses the default duration.
+ animateCollapseShade()
+ }
+
private fun animateCollapseShadeInternal() {
sceneInteractor.changeScene(
getCollapseDestinationScene(),
@@ -143,9 +149,9 @@ constructor(
private fun getCollapseDestinationScene(): SceneKey {
return if (deviceEntryInteractor.isDeviceEntered.value) {
- SceneKey.Gone
+ Scenes.Gone
} else {
- SceneKey.Lockscreen
+ Scenes.Lockscreen
}
}
@@ -183,11 +189,11 @@ constructor(
}
override fun expandToNotifications() {
- sceneInteractor.changeScene(SceneKey.Shade, "ShadeController.animateExpandShade")
+ sceneInteractor.changeScene(Scenes.Shade, "ShadeController.animateExpandShade")
}
override fun expandToQs() {
- sceneInteractor.changeScene(SceneKey.QuickSettings, "ShadeController.animateExpandQs")
+ sceneInteractor.changeScene(Scenes.QuickSettings, "ShadeController.animateExpandQs")
}
override fun setVisibilityListener(listener: ShadeVisibilityListener) {
@@ -237,7 +243,7 @@ constructor(
}
override fun isExpandedVisible(): Boolean {
- return sceneInteractor.currentScene.value != SceneKey.Gone
+ return sceneInteractor.currentScene.value != Scenes.Gone
}
override fun onStatusBarTouch(event: MotionEvent) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 5632766f2633..504dbfdafd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.shade.data.repository.PrivacyChipRepository
import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
@@ -111,6 +113,26 @@ abstract class ShadeModule {
sceneContainerOff.get()
}
}
+
+ @Provides
+ @SysUISingleton
+ fun provideQuickSettingsController(
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerOn: Provider<QuickSettingsControllerSceneImpl>,
+ sceneContainerOff: Provider<QuickSettingsControllerImpl>,
+ ): QuickSettingsController {
+ return if (sceneContainerFlags.isEnabled()) {
+ sceneContainerOn.get()
+ } else {
+ sceneContainerOff.get()
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesQSContainerController(impl: QSSceneAdapterImpl): QSContainerController {
+ return impl
+ }
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 44c6a82d93ca..7a1637eeab15 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -31,12 +31,6 @@ import java.util.function.Consumer
* @see NotificationPanelViewController
*/
interface ShadeViewController {
- /** Expand the shade either animated or instantly. */
- fun expand(animate: Boolean)
-
- /** Animates to an expanded shade with QS expanded. If the shade starts expanded, expands QS. */
- fun expandToQs()
-
/** Returns whether the shade is expanding or collapsing itself or quick settings. */
val isExpandingOrCollapsing: Boolean
@@ -58,9 +52,6 @@ interface ShadeViewController {
/** Collapses the shade. */
fun collapse(animate: Boolean, delayed: Boolean, speedUpFactor: Float)
- /** Collapses the shade with an animation duration in milliseconds. */
- fun collapseWithDuration(animationDuration: Int)
-
/** Collapses the shade instantly without animation. */
fun instantCollapse()
@@ -102,9 +93,6 @@ interface ShadeViewController {
/** Returns the StatusBarState. */
val barState: Int
- /** Sets the amount of progress in the status bar launch animation. */
- fun applyLaunchAnimationProgress(linearProgress: Float)
-
/** Sets the alpha value of the shade to a value between 0 and 255. */
fun setAlpha(alpha: Int, animate: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 7a181f106514..3be3f6b1441d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -28,8 +28,6 @@ import javax.inject.Inject
/** Empty implementation of ShadeViewController for variants with no shade. */
class ShadeViewControllerEmptyImpl @Inject constructor() :
ShadeViewController, ShadeBackActionInteractor, ShadeLockscreenInteractor {
- override fun expand(animate: Boolean) {}
- override fun expandToQs() {}
override fun expandToNotifications() {}
override val isExpandingOrCollapsing: Boolean = false
override val isExpanded: Boolean = false
@@ -37,7 +35,6 @@ class ShadeViewControllerEmptyImpl @Inject constructor() :
override val isShadeFullyExpanded: Boolean = false
override fun collapse(delayed: Boolean, speedUpFactor: Float) {}
override fun collapse(animate: Boolean, delayed: Boolean, speedUpFactor: Float) {}
- override fun collapseWithDuration(animationDuration: Int) {}
override fun instantCollapse() {}
override fun animateCollapseQs(fullyCollapse: Boolean) {}
override fun canBeCollapsed(): Boolean = false
@@ -55,7 +52,6 @@ class ShadeViewControllerEmptyImpl @Inject constructor() :
override fun dozeTimeTick() {}
override fun resetViews(animate: Boolean) {}
override val barState: Int = 0
- override fun applyLaunchAnimationProgress(linearProgress: Float) {}
override fun closeUserSwitcherIfOpen(): Boolean {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
index 1ee6d3845553..eaac8ae9dd3a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
@@ -16,10 +16,10 @@
package com.android.systemui.shade.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeAnimationRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -44,10 +44,10 @@ constructor(
is ObservableTransitionState.Idle -> flowOf(false)
is ObservableTransitionState.Transition ->
if (
- (state.fromScene == SceneKey.Shade &&
- state.toScene != SceneKey.QuickSettings) ||
- (state.fromScene == SceneKey.QuickSettings &&
- state.toScene != SceneKey.Shade)
+ (state.fromScene == Scenes.Shade &&
+ state.toScene != Scenes.QuickSettings) ||
+ (state.fromScene == Scenes.QuickSettings &&
+ state.toScene != Scenes.Shade)
) {
state.isUserInputOngoing.map { !it }
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
index a2e25983e68f..3a8ba7a0696b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
@@ -18,7 +18,7 @@ package com.android.systemui.shade.domain.interactor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,12 +36,12 @@ constructor(
val key =
if (fullyCollapse) {
if (deviceEntryInteractor.isDeviceEntered.value) {
- SceneKey.Gone
+ Scenes.Gone
} else {
- SceneKey.Lockscreen
+ Scenes.Lockscreen
}
} else {
- SceneKey.Shade
+ Scenes.Shade
}
sceneInteractor.changeScene(key, "animateCollapseQs")
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 08f2c402f9e9..67cac3d4111c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -16,11 +16,12 @@
package com.android.systemui.shade.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -45,9 +46,9 @@ constructor(
sceneInteractor: SceneInteractor,
sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
) : BaseShadeInteractor {
- override val shadeExpansion: Flow<Float> = sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
+ override val shadeExpansion: Flow<Float> = sceneBasedExpansion(sceneInteractor, Scenes.Shade)
- private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
+ private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings)
override val qsExpansion: StateFlow<Float> =
combine(
@@ -75,7 +76,7 @@ constructor(
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.toScene == SceneKey.QuickSettings && state.fromScene != SceneKey.Shade
+ state.toScene == Scenes.QuickSettings && state.fromScene != Scenes.Shade
}
}
.distinctUntilChanged()
@@ -84,7 +85,7 @@ constructor(
sceneInteractor.transitionState
.map { state ->
when (state) {
- is ObservableTransitionState.Idle -> state.scene == SceneKey.QuickSettings
+ is ObservableTransitionState.Idle -> state.scene == Scenes.QuickSettings
is ObservableTransitionState.Transition -> false
}
}
@@ -100,10 +101,10 @@ constructor(
.stateIn(scope, SharingStarted.Eagerly, false)
override val isUserInteractingWithShade: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, SceneKey.Shade)
+ sceneBasedInteracting(sceneInteractor, Scenes.Shade)
override val isUserInteractingWithQs: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, SceneKey.QuickSettings)
+ sceneBasedInteracting(sceneInteractor, Scenes.QuickSettings)
/**
* Returns a flow that uses scene transition progress to and from a scene that is pulled down
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index 21a782e43b78..1f78ae8b6e99 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -19,7 +19,7 @@ package com.android.systemui.shade.domain.interactor
import com.android.keyguard.LockIconViewController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ShadeLockscreenInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -87,7 +87,7 @@ constructor(
private fun changeToShadeScene() {
sceneInteractor.changeScene(
- SceneKey.Shade,
+ Scenes.Shade,
"ShadeLockscreenInteractorImpl.expandToNotifications",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 38358a8f244e..c9aa51c31060 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -16,12 +16,13 @@
package com.android.systemui.shade.ui.viewmodel
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -67,7 +68,7 @@ constructor(
/** Whether or not the shade container should be clickable. */
val isClickable: StateFlow<Boolean> =
upDestinationSceneKey
- .map { it == SceneKey.Lockscreen }
+ .map { it == Scenes.Lockscreen }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
@@ -82,9 +83,9 @@ constructor(
canSwipeToDismiss: Boolean?,
): SceneKey {
return when {
- canSwipeToDismiss == true -> SceneKey.Lockscreen
- isUnlocked -> SceneKey.Gone
- else -> SceneKey.Lockscreen
+ canSwipeToDismiss == true -> Scenes.Lockscreen
+ isUnlocked -> Scenes.Gone
+ else -> Scenes.Lockscreen
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index a4741a509d72..a12b9709a063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -84,6 +84,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -457,14 +458,43 @@ public final class KeyboardShortcutListSearch {
List<KeyboardShortcutMultiMappingGroup> keyboardShortcutMultiMappingGroups =
new ArrayList<>();
for (KeyboardShortcutGroup group : keyboardShortcutGroups) {
- CharSequence categoryTitle = group.getLabel();
- List<ShortcutMultiMappingInfo> shortcutMultiMappingInfos = new ArrayList<>();
+ KeyboardShortcutMultiMappingGroup mappedGroup =
+ new KeyboardShortcutMultiMappingGroup(
+ group.getLabel(),
+ new ArrayList<>());
+ Map<String, List<ShortcutMultiMappingInfo>> shortcutMap = new LinkedHashMap<>();
for (KeyboardShortcutInfo info : group.getItems()) {
- shortcutMultiMappingInfos.add(new ShortcutMultiMappingInfo(info));
+ String label = info.getLabel().toString();
+ Icon icon = info.getIcon();
+ if (shortcutMap.containsKey(label)) {
+ List<ShortcutMultiMappingInfo> shortcuts = shortcutMap.get(label);
+ boolean foundSameIcon = false;
+ for (ShortcutMultiMappingInfo shortcut : shortcuts) {
+ Icon shortcutIcon = shortcut.getIcon();
+ if ((shortcutIcon != null
+ && icon != null
+ && shortcutIcon.sameAs(icon))
+ || (shortcutIcon == null && icon == null)) {
+ foundSameIcon = true;
+ shortcut.addShortcutKeyGroup(new ShortcutKeyGroup(info, null));
+ break;
+ }
+ }
+ if (!foundSameIcon) {
+ shortcuts.add(new ShortcutMultiMappingInfo(info));
+ }
+ } else {
+ List<ShortcutMultiMappingInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(new ShortcutMultiMappingInfo(info));
+ shortcutMap.put(label, shortcuts);
+ }
}
- keyboardShortcutMultiMappingGroups.add(
- new KeyboardShortcutMultiMappingGroup(
- categoryTitle, shortcutMultiMappingInfos));
+ for (List<ShortcutMultiMappingInfo> shortcutInfos : shortcutMap.values()) {
+ for (ShortcutMultiMappingInfo shortcutInfo : shortcutInfos) {
+ mappedGroup.addItem(shortcutInfo);
+ }
+ }
+ keyboardShortcutMultiMappingGroups.add(mappedGroup);
}
return keyboardShortcutMultiMappingGroups;
}
@@ -482,42 +512,42 @@ public final class KeyboardShortcutListSearch {
context.getString(R.string.keyboard_shortcut_group_system),
new ArrayList<>());
List<ShortcutKeyGroupMultiMappingInfo> infoList = Arrays.asList(
- /* Access notification shade: Meta + N */
+ /* Access list of all apps and search (i.e. Search/Launcher): Meta */
new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_access_notification_shade),
+ context.getString(R.string.group_system_access_all_apps_search),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_N, KeyEvent.META_META_ON))),
- /* Take a full screenshot: Meta + Ctrl + S */
+ Pair.create(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON))),
+ /* Access home screen: Meta + H, Meta + Enter */
new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_full_screenshot),
+ context.getString(R.string.group_system_access_home_screen),
Arrays.asList(
- Pair.create(
- KeyEvent.KEYCODE_S,
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))),
- /* Access list of system / apps shortcuts: Meta + / */
+ Pair.create(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON),
+ Pair.create(KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON))),
+ /* Overview of open apps: Meta + Tab */
new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_access_system_app_shortcuts),
+ context.getString(R.string.group_system_overview_open_apps),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))),
+ Pair.create(KeyEvent.KEYCODE_TAB, KeyEvent.META_META_ON))),
/* Back: go back to previous state (back button) */
- /* Meta + Escape, Meta + Grave, Meta + backspace, Meta + left arrow */
+ /* Meta + Escape, Meta + backspace, Meta + left arrow */
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_go_back),
Arrays.asList(
Pair.create(KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_META_ON),
Pair.create(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON),
Pair.create(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_META_ON))),
- /* Access home screen: Meta + H, Meta + Enter */
+ /* Take a full screenshot: Meta + Ctrl + S */
new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_access_home_screen),
+ context.getString(R.string.group_system_full_screenshot),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_H, KeyEvent.META_META_ON),
- Pair.create(KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON))),
- /* Overview of open apps: Meta + Tab */
+ Pair.create(
+ KeyEvent.KEYCODE_S,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))),
+ /* Access list of system / apps shortcuts: Meta + / */
new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_overview_open_apps),
+ context.getString(R.string.group_system_access_system_app_shortcuts),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_TAB, KeyEvent.META_META_ON))),
+ Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))),
/* Cycle through recent apps (forward): Alt + Tab */
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_cycle_forward),
@@ -530,26 +560,16 @@ public final class KeyboardShortcutListSearch {
Pair.create(
KeyEvent.KEYCODE_TAB,
KeyEvent.META_SHIFT_ON | KeyEvent.META_ALT_ON))),
- /* Access list of all apps and search (i.e. Search/Launcher): Meta */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_access_all_apps_search),
- Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON))),
/* Hide and (re)show taskbar: Meta + T */
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_hide_reshow_taskbar),
Arrays.asList(
Pair.create(KeyEvent.KEYCODE_T, KeyEvent.META_META_ON))),
- /* Access system settings: Meta + I */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_access_system_settings),
- Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_I, KeyEvent.META_META_ON))),
- /* Access Google Assistant: Meta + A */
+ /* Access notification shade: Meta + N */
new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_access_google_assistant),
+ context.getString(R.string.group_system_access_notification_shade),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON))),
+ Pair.create(KeyEvent.KEYCODE_N, KeyEvent.META_META_ON))),
/* Lock screen: Meta + L */
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_lock_screen),
@@ -561,7 +581,17 @@ public final class KeyboardShortcutListSearch {
Arrays.asList(
Pair.create(
KeyEvent.KEYCODE_N,
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON)))
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))),
+ /* Access system settings: Meta + I */
+ new ShortcutKeyGroupMultiMappingInfo(
+ context.getString(R.string.group_system_access_system_settings),
+ Arrays.asList(
+ Pair.create(KeyEvent.KEYCODE_I, KeyEvent.META_META_ON))),
+ /* Access Google Assistant: Meta + A */
+ new ShortcutKeyGroupMultiMappingInfo(
+ context.getString(R.string.group_system_access_google_assistant),
+ Arrays.asList(
+ Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON)))
);
for (ShortcutKeyGroupMultiMappingInfo info : infoList) {
systemGroup.addItem(info.getShortcutMultiMappingInfo());
@@ -1377,7 +1407,9 @@ public final class KeyboardShortcutListSearch {
ShortcutMultiMappingInfo(KeyboardShortcutInfo info) {
mLabel = info.getLabel();
mIcon = info.getIcon();
- mShortcutKeyGroups = Arrays.asList(new ShortcutKeyGroup(info, null));
+ mShortcutKeyGroups = new ArrayList<>(
+ Arrays.asList(new ShortcutKeyGroup(info, null))
+ );
}
CharSequence getLabel() {
@@ -1388,6 +1420,10 @@ public final class KeyboardShortcutListSearch {
return mIcon;
}
+ void addShortcutKeyGroup(ShortcutKeyGroup group) {
+ mShortcutKeyGroups.add(group);
+ }
+
List<ShortcutKeyGroup> getShortcutKeyGroups() {
return mShortcutKeyGroups;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 19fe60a60bf5..1ec86aea49d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -71,6 +71,7 @@ import android.os.UserManager;
import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.text.format.Formatter;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
@@ -187,6 +188,7 @@ public class KeyguardIndicationController {
private CharSequence mTransientIndication;
private CharSequence mBiometricMessage;
private CharSequence mBiometricMessageFollowUp;
+ private BiometricSourceType mBiometricMessageSource;
protected ColorStateList mInitialTextColorState;
private boolean mVisible;
private boolean mOrganizationOwnedDevice;
@@ -206,7 +208,7 @@ public class KeyguardIndicationController {
private int mBatteryLevel;
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
- private String mBiometricErrorMessageToShowOnScreenOn;
+ private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
private boolean mInited;
@@ -225,15 +227,18 @@ public class KeyguardIndicationController {
mIsActiveDreamLockscreenHosted = isLockscreenHosted;
updateDeviceEntryIndication(false);
};
- private final ScreenLifecycle.Observer mScreenObserver =
- new ScreenLifecycle.Observer() {
+ private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurnedOn() {
mHandler.removeMessages(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON);
if (mBiometricErrorMessageToShowOnScreenOn != null) {
String followUpMessage = mFaceLockedOutThisAuthSession
? faceLockedOutFollowupMessage() : null;
- showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn, followUpMessage);
+ showBiometricMessage(
+ mBiometricErrorMessageToShowOnScreenOn.first,
+ followUpMessage,
+ mBiometricErrorMessageToShowOnScreenOn.second
+ );
// We want to keep this message around in case the screen was off
hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS);
mBiometricErrorMessageToShowOnScreenOn = null;
@@ -879,8 +884,35 @@ public class KeyguardIndicationController {
updateTransient();
}
- private void showBiometricMessage(CharSequence biometricMessage) {
- showBiometricMessage(biometricMessage, null);
+ private void showSuccessBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType
+ ) {
+ showBiometricMessage(biometricMessage, biometricMessageFollowUp, biometricSourceType, true);
+ }
+
+ private void showSuccessBiometricMessage(CharSequence biometricMessage,
+ BiometricSourceType biometricSourceType) {
+ showSuccessBiometricMessage(biometricMessage, null, biometricSourceType);
+ }
+
+ private void showBiometricMessage(CharSequence biometricMessage,
+ BiometricSourceType biometricSourceType) {
+ showBiometricMessage(biometricMessage, null, biometricSourceType, false);
+ }
+
+ private void showBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType
+ ) {
+ showBiometricMessage(
+ biometricMessage,
+ biometricMessageFollowUp,
+ biometricSourceType,
+ false
+ );
}
/**
@@ -889,15 +921,33 @@ public class KeyguardIndicationController {
* by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message
* logic.
*/
- private void showBiometricMessage(CharSequence biometricMessage,
- @Nullable CharSequence biometricMessageFollowUp) {
+ private void showBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType,
+ boolean isSuccessMessage
+ ) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)
+ && biometricSourceType == mBiometricMessageSource
&& TextUtils.equals(biometricMessageFollowUp, mBiometricMessageFollowUp)) {
return;
}
+ if (!isSuccessMessage
+ && mBiometricMessageSource == FINGERPRINT
+ && biometricSourceType != FINGERPRINT) {
+ // drop all non-fingerprint biometric messages if there's a fingerprint message showing
+ mKeyguardLogger.logDropNonFingerprintMessage(
+ biometricMessage,
+ biometricMessageFollowUp,
+ biometricSourceType
+ );
+ return;
+ }
+
mBiometricMessage = biometricMessage;
mBiometricMessageFollowUp = biometricMessageFollowUp;
+ mBiometricMessageSource = biometricSourceType;
mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
hideBiometricMessageDelayed(
@@ -914,6 +964,7 @@ public class KeyguardIndicationController {
if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
mBiometricMessage = null;
mBiometricMessageFollowUp = null;
+ mBiometricMessageSource = null;
mHideBiometricMessageHandler.cancel();
updateBiometricMessage();
}
@@ -1085,7 +1136,8 @@ public class KeyguardIndicationController {
} else {
message = mContext.getString(R.string.keyguard_retry);
}
- mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
+ null);
}
} else {
final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -1097,34 +1149,40 @@ public class KeyguardIndicationController {
|| mAccessibilityManager.isTouchExplorationEnabled();
if (udfpsSupported && faceAuthenticated) { // co-ex
if (a11yEnabled) {
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else {
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock_press)
+ mContext.getString(R.string.keyguard_unlock_press),
+ FACE
);
}
} else if (faceAuthenticated) { // face-only
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else if (udfpsSupported) { // udfps-only
if (a11yEnabled) {
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showSuccessBiometricMessage(
+ mContext.getString(R.string.keyguard_unlock),
+ null
+ );
} else {
- showBiometricMessage(mContext.getString(
- R.string.keyguard_unlock_press));
+ showSuccessBiometricMessage(mContext.getString(
+ R.string.keyguard_unlock_press), null);
}
} else { // no security or unlocked by a trust agent
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showSuccessBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
}
} else {
// suggest swiping up for the primary authentication bouncer
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
}
}
}
@@ -1228,6 +1286,13 @@ public class KeyguardIndicationController {
&& msgId != BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
final boolean faceAuthFailed = biometricSourceType == FACE
&& msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
+ if (faceAuthFailed && mFaceLockedOutThisAuthSession) {
+ mKeyguardLogger.logBiometricMessage(
+ "skipped showing faceAuthFailed message due to lockout",
+ msgId,
+ helpString);
+ return;
+ }
final boolean fpAuthFailed = biometricSourceType == FINGERPRINT
&& msgId == BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; // ran matcher & failed
final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
@@ -1245,49 +1310,55 @@ public class KeyguardIndicationController {
mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
}
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
- mInitialTextColorState);
+ mInitialTextColorState, biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
showBiometricMessage(
helpString,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ biometricSourceType
);
} else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
showBiometricMessage(
mContext.getString(R.string.keyguard_face_failed),
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ biometricSourceType
);
} else if (fpAuthFailed
&& mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()) {
// face had already previously unlocked the device, so instead of showing a
// fingerprint error, tell them they have already unlocked with face auth
// and how to enter their device
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ null
);
} else if (fpAuthFailed
&& mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())) {
- showBiometricMessage(
+ showSuccessBiometricMessage(
getTrustGrantedIndication(),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ null
);
} else if (faceAuthUnavailable) {
showBiometricMessage(
helpString,
isUnlockWithFingerprintPossible
? mContext.getString(R.string.keyguard_suggest_fingerprint)
- : mContext.getString(R.string.keyguard_unlock)
+ : mContext.getString(R.string.keyguard_unlock),
+ biometricSourceType
);
} else {
- showBiometricMessage(helpString);
+ showBiometricMessage(helpString, biometricSourceType);
}
} else if (faceAuthFailed) {
// show action to unlock
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
} else {
- mBiometricErrorMessageToShowOnScreenOn = helpString;
+ mBiometricErrorMessageToShowOnScreenOn =
+ new Pair<>(helpString, biometricSourceType);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON),
1000);
@@ -1333,7 +1404,7 @@ public class KeyguardIndicationController {
} else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) {
handleFaceLockoutError(errString);
} else {
- showErrorMessageNowOrLater(errString, null);
+ showErrorMessageNowOrLater(errString, null, FACE);
}
}
@@ -1343,7 +1414,7 @@ public class KeyguardIndicationController {
msgId,
errString);
} else {
- showErrorMessageNowOrLater(errString, null);
+ showErrorMessageNowOrLater(errString, null, FINGERPRINT);
}
}
@@ -1371,7 +1442,7 @@ public class KeyguardIndicationController {
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
- showBiometricMessage(message);
+ showBiometricMessage(message, null);
}
@Override
@@ -1459,12 +1530,13 @@ public class KeyguardIndicationController {
// had too many unsuccessful attempts.
if (!mFaceLockedOutThisAuthSession) {
mFaceLockedOutThisAuthSession = true;
- showErrorMessageNowOrLater(errString, followupMessage);
+ showErrorMessageNowOrLater(errString, followupMessage, FACE);
} else if (!mAuthController.isUdfpsFingerDown()) {
// On subsequent lockouts, we show a more generic locked out message.
showErrorMessageNowOrLater(
mContext.getString(R.string.keyguard_face_unlock_unavailable),
- followupMessage);
+ followupMessage,
+ FACE);
}
}
@@ -1484,7 +1556,8 @@ public class KeyguardIndicationController {
&& !mStatusBarKeyguardViewManager.isBouncerShowing()) {
showBiometricMessage(
deferredFaceMessage,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ FACE
);
} else {
// otherwise, don't show any message
@@ -1496,7 +1569,8 @@ public class KeyguardIndicationController {
// user to manually retry.
showBiometricMessage(
deferredFaceMessage,
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else {
// Face-only
@@ -1510,13 +1584,15 @@ public class KeyguardIndicationController {
getCurrentUser()) && mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed();
}
- private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
+ private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
+ BiometricSourceType biometricSourceType) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
+ biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- showBiometricMessage(errString, followUpMsg);
+ showBiometricMessage(errString, followUpMsg, biometricSourceType);
} else {
- mBiometricErrorMessageToShowOnScreenOn = errString;
+ mBiometricErrorMessageToShowOnScreenOn = new Pair<>(errString, biometricSourceType);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 143fc324f8c7..3cf61e211e42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -34,6 +34,8 @@ import com.android.internal.widget.ConversationLayout;
import com.android.internal.widget.ImageFloatingTextView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentView;
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import java.util.ArrayList;
import java.util.HashSet;
@@ -253,7 +255,8 @@ public class NotificationGroupingUtil {
}
public void init() {
- View header = mParentRow.getNotificationViewWrapper().getNotificationHeader();
+ NotificationViewWrapper wrapper = mParentRow.getNotificationViewWrapper();
+ View header = wrapper == null ? null : wrapper.getNotificationHeader();
mParentView = header == null ? null : header.findViewById(mId);
mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow);
mApply = !mComparator.isEmpty(mParentView);
@@ -326,6 +329,9 @@ public class NotificationGroupingUtil {
@Override
public boolean isEmpty(View view) {
+ if (AsyncGroupHeaderViewInflation.isEnabled() && view == null) {
+ return true;
+ }
if (view instanceof ImageView) {
return ((ImageView) view).getDrawable() == null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 36fc9bb3a2da..e0dd7f0603e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -36,6 +36,7 @@ import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import com.android.app.animation.Interpolators;
+import com.android.compose.animation.scene.SceneKey;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
@@ -49,7 +50,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController.StateList
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.scene.shared.model.SceneKey;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -659,11 +660,11 @@ public class StatusBarStateControllerImpl implements
}
private static final Map<SceneKey, Integer> sStatusBarStateByLockedSceneKey = Map.of(
- SceneKey.Lockscreen.INSTANCE, StatusBarState.KEYGUARD,
- SceneKey.Bouncer.INSTANCE, StatusBarState.KEYGUARD,
- SceneKey.Communal.INSTANCE, StatusBarState.KEYGUARD,
- SceneKey.Shade.INSTANCE, StatusBarState.SHADE_LOCKED,
- SceneKey.QuickSettings.INSTANCE, StatusBarState.SHADE_LOCKED
+ Scenes.Lockscreen, StatusBarState.KEYGUARD,
+ Scenes.Bouncer, StatusBarState.KEYGUARD,
+ Scenes.Communal, StatusBarState.KEYGUARD,
+ Scenes.Shade, StatusBarState.SHADE_LOCKED,
+ Scenes.QuickSettings, StatusBarState.SHADE_LOCKED
);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
new file mode 100644
index 000000000000..1bebbfd34ff4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/**
+ * Whether to set the status bar keyguard view occluded or not, and whether to animate that change.
+ */
+data class OccludedState(
+ val occluded: Boolean,
+ val animate: Boolean = false,
+)
+
+/** Handles logic around calls to [StatusBarKeyguardViewManager] in legacy code. */
+@Deprecated("Will be removed once all of SBKVM's responsibilies are refactored.")
+@SysUISingleton
+class StatusBarKeyguardViewManagerInteractor
+@Inject
+constructor(
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ powerInteractor: PowerInteractor,
+) {
+ /** Occlusion state to apply whenever a keyguard transition is STARTED, if any. */
+ private val occlusionStateFromStartedStep: Flow<OccludedState> =
+ keyguardTransitionInteractor.startedKeyguardTransitionStep
+ .sample(powerInteractor.detailedWakefulness, ::Pair)
+ .map { (startedStep, wakefulness) ->
+ val transitioningFromPowerButtonGesture =
+ KeyguardState.deviceIsAsleepInState(startedStep.from) &&
+ startedStep.to == KeyguardState.OCCLUDED &&
+ wakefulness.powerButtonLaunchGestureTriggered
+
+ if (
+ startedStep.to == KeyguardState.OCCLUDED && !transitioningFromPowerButtonGesture
+ ) {
+ // Set occluded upon STARTED, *unless* we're transitioning from the power
+ // button, in which case we're going to play an animation over the lockscreen UI
+ // and need to remain unoccluded until the transition finishes.
+ return@map OccludedState(occluded = true, animate = false)
+ }
+
+ if (
+ startedStep.from == KeyguardState.OCCLUDED &&
+ startedStep.to == KeyguardState.LOCKSCREEN
+ ) {
+ // Set unoccluded immediately ONLY if we're transitioning back to the lockscreen
+ // since we need the views visible to animate them back down. This is a special
+ // case due to the way unocclusion remote animations are run. We can remove this
+ // once the unocclude animation uses the return animation framework.
+ return@map OccludedState(occluded = false, animate = false)
+ }
+
+ // Otherwise, wait for the transition to FINISH to decide.
+ return@map null
+ }
+ .filterNotNull()
+
+ /** Occlusion state to apply whenever a keyguard transition is FINISHED. */
+ private val occlusionStateFromFinishedStep =
+ keyguardTransitionInteractor.finishedKeyguardTransitionStep
+ .sample(keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, ::Pair)
+ .map { (finishedStep, showWhenLockedOnTop) ->
+ // If we're FINISHED in OCCLUDED, we want to render as occluded. We also need to
+ // remain occluded if a SHOW_WHEN_LOCKED activity is on the top of the task stack,
+ // and we're in any state other than GONE. This is necessary, for example, when we
+ // transition from OCCLUDED to a bouncer state. Otherwise, we should not be
+ // occluded.
+ val occluded =
+ finishedStep.to == KeyguardState.OCCLUDED ||
+ (showWhenLockedOnTop && finishedStep.to != KeyguardState.GONE)
+ OccludedState(occluded = occluded, animate = false)
+ }
+
+ /** Occlusion state to apply to SKBVM's setOccluded call. */
+ val keyguardViewOcclusionState =
+ merge(occlusionStateFromStartedStep, occlusionStateFromFinishedStep)
+ .distinctUntilChangedBy {
+ // Don't switch 'animate' values mid-transition.
+ it.occluded
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 639e23ae0765..deaf1d1bc764 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -363,10 +363,11 @@ public class PreparationCoordinator implements Coordinator {
NotifInflater.Params getInflaterParams(NotifUiAdjustment adjustment, String reason) {
return new NotifInflater.Params(
- adjustment.isMinimized(),
- reason,
- adjustment.isSnoozeEnabled(),
- adjustment.isChildInGroup()
+ /* isLowPriority = */ adjustment.isMinimized(),
+ /* reason = */ reason,
+ /* showSnooze = */ adjustment.isSnoozeEnabled(),
+ /* isChildInGroup = */ adjustment.isChildInGroup(),
+ /* isGroupSummary = */ adjustment.isGroupSummary()
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 29627e14e4a0..f03c3139ca8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -119,6 +119,9 @@ class SensitiveContentCoordinatorImpl @Inject constructor(
val needsRedaction = lockscreenUserManager.needsRedaction(entry)
val isSensitive = userPublic && needsRedaction
entry.setSensitive(isSensitive || shouldProtectNotification, deviceSensitive)
+ if (screenshareNotificationHiding()) {
+ entry.row?.setPublicExpanderVisible(!shouldProtectNotification)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index c0b187be42f3..18460c3e3766 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -60,5 +60,6 @@ interface NotifInflater {
val reason: String,
val showSnooze: Boolean,
val isChildInGroup: Boolean = false,
+ val isGroupSummary: Boolean = false,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
index e1d2cdc65d5a..bab94b50018e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -20,6 +20,7 @@ import android.app.Notification
import android.app.RemoteInput
import android.graphics.drawable.Icon
import android.text.TextUtils
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation
/**
@@ -36,6 +37,7 @@ class NotifUiAdjustment internal constructor(
val isMinimized: Boolean,
val needsRedaction: Boolean,
val isChildInGroup: Boolean,
+ val isGroupSummary: Boolean,
) {
companion object {
@JvmStatic
@@ -55,6 +57,8 @@ class NotifUiAdjustment internal constructor(
// !oldAdjustment.isChildInGroup && newAdjustment.isChildInGroup -> true
AsyncHybridViewInflation.isEnabled &&
oldAdjustment.isChildInGroup != newAdjustment.isChildInGroup -> true
+ AsyncGroupHeaderViewInflation.isEnabled &&
+ !oldAdjustment.isGroupSummary && newAdjustment.isGroupSummary -> true
else -> false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 6f44c13a3e71..0b9d19df3a75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -123,6 +123,7 @@ class NotifUiAdjustmentProvider @Inject constructor(
isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
isMinimized = isEntryMinimized(entry),
needsRedaction = lockscreenUserManager.needsRedaction(entry),
- isChildInGroup = groupMembershipManager.isChildInGroup(entry),
+ isChildInGroup = entry.sbn.isAppOrSystemGroupChild,
+ isGroupSummary = entry.sbn.isAppOrSystemGroupSummary,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 954e80505cbe..c5b55c7b1d9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -21,6 +21,8 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER;
import static java.util.Objects.requireNonNull;
@@ -50,6 +52,7 @@ import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -271,6 +274,17 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
}
}
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ if (inflaterParams.isGroupSummary()) {
+ params.requireContentViews(FLAG_GROUP_SUMMARY_HEADER);
+ if (isLowPriority) {
+ params.requireContentViews(FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER);
+ }
+ } else {
+ params.markContentViewsFreeable(FLAG_GROUP_SUMMARY_HEADER);
+ params.markContentViewsFreeable(FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER);
+ }
+ }
params.rebindAllContentViews();
mLogger.logRequestingRebind(entry, inflaterParams);
mRowContentBindStage.requestRebind(entry, en -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 73580343fab7..61cdea190a43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -547,13 +547,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
private void setContentAlpha(float contentAlpha) {
- View contentView = getContentView();
- if (contentView.hasOverlappingRendering()) {
- int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
- : LAYER_TYPE_HARDWARE;
- contentView.setLayerType(layerType, null);
- }
- contentView.setAlpha(contentAlpha);
+ setAlphaAndLayerType(getContentView(), contentAlpha);
// After updating the current view, reset all views.
if (contentAlpha == 1f) {
resetAllContentAlphas();
@@ -561,6 +555,19 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
/**
+ * Set a content view's alpha value and hardware layer type for fluent animations
+ * @param contentView the view to set
+ * @param alpha the alpha value to set
+ */
+ protected void setAlphaAndLayerType(View contentView, float alpha) {
+ if (contentView.hasOverlappingRendering()) {
+ int layerType = alpha == 0.0f || alpha == 1.0f ? LAYER_TYPE_NONE : LAYER_TYPE_HARDWARE;
+ contentView.setLayerType(layerType, null);
+ }
+ contentView.setAlpha(alpha);
+ }
+
+ /**
* If a subclass's {@link #getContentView()} returns different views depending on state,
* this method is an opportunity to reset the alpha of ALL content views, not just the
* current one, which may prevent a content view that is temporarily hidden from being reset.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index decb244947ff..5f3a83aa35e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -100,7 +100,9 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -218,6 +220,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mShowingPublic;
private boolean mSensitive;
private boolean mSensitiveHiddenInGeneral;
+ private boolean mShowPublicExpander = true;
private boolean mShowingPublicInitialized;
private boolean mHideSensitiveForIntrinsicHeight;
private float mHeaderVisibleAmount = DEFAULT_HEADER_VISIBLE_AMOUNT;
@@ -586,7 +589,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mMenuRow.setAppName(mAppName);
}
if (mIsSummaryWithChildren) {
- mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation());
+ if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+ // We create the header from the background thread instead
+ mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
+ isConversation());
+ }
mChildrenContainer.onNotificationUpdated();
}
if (mAnimationRunning) {
@@ -599,8 +606,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNotificationParent.updateChildrenAppearance();
}
onAttachedChildrenCountChanged();
- // The public layouts expand button is always visible
- mPublicLayout.updateExpandButtons(true);
+ mPublicLayout.updateExpandButtons(mShowPublicExpander);
updateLimits();
updateShelfIconColor();
if (mUpdateSelfBackgroundOnUpdate) {
@@ -668,7 +674,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public int getOriginalIconColor() {
if (mIsSummaryWithChildren && !shouldShowPublic()) {
- return mChildrenContainer.getVisibleWrapper().getOriginalIconColor();
+ if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+ return mChildrenContainer.getVisibleWrapper().getOriginalIconColor();
+ }
}
int color = getShowingLayout().getOriginalIconColor();
if (color != Notification.COLOR_INVALID) {
@@ -1513,6 +1521,40 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mChildrenContainer;
}
+ /**
+ * @return An non-null instance of mChildrenContainer, inflate it if not yet.
+ */
+ public @NonNull NotificationChildrenContainer getChildrenContainerNonNull() {
+ if (mChildrenContainer == null) {
+ mChildrenContainerStub.inflate();
+ }
+ return mChildrenContainer;
+ }
+
+ /**
+ * Set the group notification header view
+ * @param headerView header view to set
+ */
+ public void setGroupHeader(NotificationHeaderView headerView) {
+ NotificationChildrenContainer childrenContainer = getChildrenContainerNonNull();
+ childrenContainer.setGroupHeader(
+ /* headerView= */ headerView,
+ /* onClickListener= */ mExpandClickListener
+ );
+ }
+
+ /**
+ * Set the low-priority group notification header view
+ * @param headerView header view to set
+ */
+ public void setLowPriorityGroupHeader(NotificationHeaderView headerView) {
+ NotificationChildrenContainer childrenContainer = getChildrenContainerNonNull();
+ childrenContainer.setLowPriorityGroupHeader(
+ /* headerViewLowPriority= */ headerView,
+ /* onClickListener= */ mExpandClickListener
+ );
+ }
+
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
boolean wasAboveShelf = isAboveShelf();
boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
@@ -1565,7 +1607,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public View getShelfTransformationTarget() {
if (mIsSummaryWithChildren && !shouldShowPublic()) {
- return mChildrenContainer.getVisibleWrapper().getShelfTransformationTarget();
+ NotificationViewWrapper viewWrapper = mChildrenContainer.getVisibleWrapper();
+ if (AsyncGroupHeaderViewInflation.isEnabled() && viewWrapper == null) {
+ return null;
+ }
+ return viewWrapper.getShelfTransformationTarget();
}
return getShowingLayout().getShelfTransformationTarget();
}
@@ -1715,7 +1761,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* Constructs an ExpandableNotificationRow. Used by layout inflation.
*
* @param context passed to image resolver
- * @param attrs attributes used to initialize parent view
+ * @param attrs attributes used to initialize parent view
*/
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
this(context, attrs, context);
@@ -1729,9 +1775,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* AsyncLayoutFactory} in {@link RowInflaterTask}.
*
* @param context context context of the view
- * @param attrs attributes used to initialize parent view
- * @param entry notification that the row will be associated to (determines the user for the
- * ImageResolver)
+ * @param attrs attributes used to initialize parent view
+ * @param entry notification that the row will be associated to (determines the user for the
+ * ImageResolver)
*/
public ExpandableNotificationRow(Context context, AttributeSet attrs, NotificationEntry entry) {
this(context, attrs, userContextForEntry(context, entry));
@@ -1982,7 +2028,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return traceTag;
}
- return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+ return traceTag + "(" + getEntry().getNotificationStyle() + ")";
}
@Override
@@ -2710,10 +2756,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
&& mChildrenContainer.getNotificationChildCount() > 0;
if (mIsSummaryWithChildren) {
Trace.beginSection("ExpNotRow#onChildCountChanged (summary)");
- NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper();
- if (wrapper == null || wrapper.getNotificationHeader() == null) {
- mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
- isConversation());
+ if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+ NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper();
+ if (wrapper == null || wrapper.getNotificationHeader() == null) {
+ mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
+ isConversation());
+ }
}
}
if (!mIsSummaryWithChildren && wasSummary) {
@@ -2837,6 +2885,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
+ /** Sets whether this notification row should show the notification expander or not */
+ public void setPublicExpanderVisible(boolean showPublicExpander) {
+ if (mShowPublicExpander != showPublicExpander) {
+ mShowPublicExpander = showPublicExpander;
+ mPublicLayout.updateExpandButtons(mShowPublicExpander);
+ }
+ }
+
@Override
public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
mHideSensitiveForIntrinsicHeight = hideSensitive;
@@ -2863,6 +2919,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
return;
}
+ float oldAlpha = getContentView().getAlpha();
if (!animated) {
mPublicLayout.animate().cancel();
@@ -2873,6 +2930,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
resetAllContentAlphas();
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
+ if (NotificationContentAlphaOptimization.isEnabled()) {
+ // We want to set the old alpha to the now-showing layout to avoid breaking an
+ // on-going animation
+ if (oldAlpha != 1f) {
+ setAlphaAndLayerType(mShowingPublic ? mPublicLayout : mPrivateLayout, oldAlpha);
+ }
+ }
} else {
animateShowingPublic(delay, duration, mShowingPublic);
}
@@ -3019,6 +3083,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
onStartedRunnable.run();
}
}
+
@Override
public void onAnimationEnd(Animator animation) {
ExpandableNotificationRow.super.performRemoveAnimation(
@@ -3713,6 +3778,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
pw.print(", mShowNoBackground: " + mShowNoBackground);
pw.println();
+ if (NotificationContentView.INCLUDE_HEIGHTS_TO_DUMP) {
+ dumpHeights(pw);
+ }
showingLayout.dump(pw, args);
if (getViewState() != null) {
@@ -3756,6 +3824,34 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
});
}
+ private void dumpHeights(IndentingPrintWriter pw) {
+ pw.print("Heights: ");
+ pw.print("intrinsic", getIntrinsicHeight());
+ pw.print("actual", getActualHeight());
+ pw.print("maxContent", getMaxContentHeight());
+ pw.print("maxExpanded", getMaxExpandHeight());
+ pw.print("collapsed", getCollapsedHeight());
+ pw.print("headsup", getHeadsUpHeight());
+ pw.print("headsup without header", getHeadsUpHeightWithoutHeader());
+ pw.print("minHeight", getMinHeight());
+ pw.print("pinned headsup", getPinnedHeadsUpHeight(
+ true /* atLeastMinHeight */));
+ pw.println();
+ pw.print("Intrinsic Height Factors: ");
+ pw.print("isUserLocked()", isUserLocked());
+ pw.print("isChildInGroup()", isChildInGroup());
+ pw.print("isGroupExpanded()", isGroupExpanded());
+ pw.print("sensitive", mSensitive);
+ pw.print("hideSensitiveForIntrinsicHeight", mHideSensitiveForIntrinsicHeight);
+ pw.print("isSummaryWithChildren", mIsSummaryWithChildren);
+ pw.print("canShowHeadsUp()", canShowHeadsUp());
+ pw.print("isHeadsUpState()", isHeadsUpState());
+ pw.print("isPinned()", isPinned());
+ pw.print("headsupDisappearRunning", mHeadsupDisappearRunning);
+ pw.print("isExpanded()", isExpanded());
+ pw.println();
+ }
+
private void logKeepInParentChildDetached(ExpandableNotificationRow child) {
if (mLogger != null) {
mLogger.logKeepInParentChildDetached(child.getEntry(), getEntry());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
index dab89c5235b0..b90aa107d617 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
@@ -19,7 +19,9 @@ package com.android.systemui.statusbar.notification.row
import android.widget.flags.Flags.notifLinearlayoutOptimized
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing
import javax.inject.Inject
+import javax.inject.Provider
interface NotifRemoteViewsFactoryContainer {
val factories: Set<NotifRemoteViewsFactory>
@@ -31,7 +33,8 @@ constructor(
featureFlags: FeatureFlags,
precomputedTextViewFactory: PrecomputedTextViewFactory,
bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory,
- optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory
+ optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory,
+ notificationViewFlipperFactory: Provider<NotificationViewFlipperFactory>,
) : NotifRemoteViewsFactoryContainer {
override val factories: Set<NotifRemoteViewsFactory> = buildSet {
add(precomputedTextViewFactory)
@@ -41,5 +44,8 @@ constructor(
if (notifLinearlayoutOptimized()) {
add(optimizedLinearLayoutFactory)
}
+ if (NotificationViewFlipperPausing.isEnabled) {
+ add(notificationViewFlipperFactory.get())
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index e288e857bf4a..c17ee3991713 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -37,7 +37,9 @@ import android.os.Trace;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.view.NotificationHeaderView;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
@@ -51,11 +53,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder;
import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder;
import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -387,6 +391,21 @@ public class NotificationContentInflater implements NotificationRowContentBinder
logger.logAsyncTaskProgress(entryForLogging, "creating public remote view");
result.newPublicView = builder.makePublicContentView(isLowPriority);
}
+
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ if ((reInflateFlags & FLAG_GROUP_SUMMARY_HEADER) != 0) {
+ logger.logAsyncTaskProgress(entryForLogging,
+ "creating group summary remote view");
+ result.mNewGroupHeaderView = builder.makeNotificationGroupHeader();
+ }
+
+ if ((reInflateFlags & FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER) != 0) {
+ logger.logAsyncTaskProgress(entryForLogging,
+ "creating low-priority group summary remote view");
+ result.mNewLowPriorityGroupHeaderView =
+ builder.makeLowPriorityContentView(true /* useRegularSubtext */);
+ }
+ }
setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider);
result.packageContext = packageContext;
result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
@@ -534,6 +553,67 @@ public class NotificationContentInflater implements NotificationRowContentBinder
runningInflations, applyCallback, logger);
}
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ NotificationChildrenContainer childrenContainer = row.getChildrenContainerNonNull();
+ if ((reInflateFlags & FLAG_GROUP_SUMMARY_HEADER) != 0) {
+ boolean isNewView =
+ !canReapplyRemoteView(
+ /* newView = */ result.mNewGroupHeaderView,
+ /* oldView = */ remoteViewCache
+ .getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER));
+ ApplyCallback applyCallback = new ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ logger.logAsyncTaskProgress(entry, "group header view applied");
+ result.mInflatedGroupHeaderView = (NotificationHeaderView) v;
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return result.mNewGroupHeaderView;
+ }
+ };
+ logger.logAsyncTaskProgress(entry, "applying group header view");
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ /* inflationId = */ FLAG_GROUP_SUMMARY_HEADER,
+ remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
+ /* parentLayout = */ childrenContainer,
+ /* existingView = */ childrenContainer.getNotificationHeader(),
+ /* existingWrapper = */ childrenContainer.getNotificationHeaderWrapper(),
+ runningInflations, applyCallback, logger);
+ }
+
+ if ((reInflateFlags & FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER) != 0) {
+ boolean isNewView =
+ !canReapplyRemoteView(
+ /* newView = */ result.mNewLowPriorityGroupHeaderView,
+ /* oldView = */ remoteViewCache.getCachedView(
+ entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER));
+ ApplyCallback applyCallback = new ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ logger.logAsyncTaskProgress(entry,
+ "low-priority group header view applied");
+ result.mInflatedLowPriorityGroupHeaderView = (NotificationHeaderView) v;
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return result.mNewLowPriorityGroupHeaderView;
+ }
+ };
+ logger.logAsyncTaskProgress(entry, "applying low priority group header view");
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ /* inflationId = */ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+ remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
+ /* parentLayout = */ childrenContainer,
+ /* existingView = */ childrenContainer.getNotificationHeaderLowPriority(),
+ /* existingWrapper = */ childrenContainer
+ .getLowPriorityViewWrapper(),
+ runningInflations, applyCallback, logger);
+ }
+ }
+
// Let's try to finish, maybe nobody is even inflating anything
finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, callback, entry,
row, logger);
@@ -560,7 +640,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
boolean isNewView,
RemoteViews.InteractionHandler remoteViewClickHandler,
@Nullable final InflationCallback callback,
- NotificationContentView parentLayout,
+ ViewGroup parentLayout,
View existingView,
NotificationViewWrapper existingWrapper,
final HashMap<Integer, CancellationSignal> runningInflations,
@@ -702,6 +782,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result;
}
+ /**
+ * Notifications with undecorated custom views need to satisfy a minimum height to avoid visual
+ * issues.
+ */
private static boolean requiresHeightCheck(NotificationEntry entry) {
// Undecorated custom views are disallowed from S onwards
if (entry.targetSdk >= Build.VERSION_CODES.S) {
@@ -749,110 +833,143 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@Nullable InflationCallback endListener, NotificationEntry entry,
ExpandableNotificationRow row, NotificationContentInflaterLogger logger) {
Assert.isMainThread();
+ if (!runningInflations.isEmpty()) {
+ return false;
+ }
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
- if (runningInflations.isEmpty()) {
- logger.logAsyncTaskProgress(entry, "finishing");
- boolean setRepliesAndActions = true;
- if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
- if (result.inflatedContentView != null) {
- // New view case
- privateLayout.setContractedChild(result.inflatedContentView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
- result.newContentView);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
- // Reinflation case. Only update if it's still cached (i.e. view has not been
- // freed while inflating).
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
- result.newContentView);
- }
- setRepliesAndActions = true;
+ logger.logAsyncTaskProgress(entry, "finishing");
+ boolean setRepliesAndActions = true;
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
+ if (result.inflatedContentView != null) {
+ // New view case
+ privateLayout.setContractedChild(result.inflatedContentView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
+ result.newContentView);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
+ // Reinflation case. Only update if it's still cached (i.e. view has not been
+ // freed while inflating).
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
+ result.newContentView);
}
+ setRepliesAndActions = true;
+ }
- if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
- if (result.inflatedExpandedView != null) {
- privateLayout.setExpandedChild(result.inflatedExpandedView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
- result.newExpandedView);
- } else if (result.newExpandedView == null) {
- privateLayout.setExpandedChild(null);
- remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
- result.newExpandedView);
- }
- if (result.newExpandedView != null) {
- privateLayout.setExpandedInflatedSmartReplies(
- result.expandedInflatedSmartReplies);
- } else {
- privateLayout.setExpandedInflatedSmartReplies(null);
- }
- row.setExpandable(result.newExpandedView != null);
- setRepliesAndActions = true;
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
+ if (result.inflatedExpandedView != null) {
+ privateLayout.setExpandedChild(result.inflatedExpandedView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
+ result.newExpandedView);
+ } else if (result.newExpandedView == null) {
+ privateLayout.setExpandedChild(null);
+ remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
+ result.newExpandedView);
+ }
+ if (result.newExpandedView != null) {
+ privateLayout.setExpandedInflatedSmartReplies(
+ result.expandedInflatedSmartReplies);
+ } else {
+ privateLayout.setExpandedInflatedSmartReplies(null);
}
+ row.setExpandable(result.newExpandedView != null);
+ setRepliesAndActions = true;
+ }
- if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
- if (result.inflatedHeadsUpView != null) {
- privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
- result.newHeadsUpView);
- } else if (result.newHeadsUpView == null) {
- privateLayout.setHeadsUpChild(null);
- remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
- result.newHeadsUpView);
- }
- if (result.newHeadsUpView != null) {
- privateLayout.setHeadsUpInflatedSmartReplies(
- result.headsUpInflatedSmartReplies);
- } else {
- privateLayout.setHeadsUpInflatedSmartReplies(null);
- }
- setRepliesAndActions = true;
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
+ if (result.inflatedHeadsUpView != null) {
+ privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
+ result.newHeadsUpView);
+ } else if (result.newHeadsUpView == null) {
+ privateLayout.setHeadsUpChild(null);
+ remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
+ result.newHeadsUpView);
}
+ if (result.newHeadsUpView != null) {
+ privateLayout.setHeadsUpInflatedSmartReplies(
+ result.headsUpInflatedSmartReplies);
+ } else {
+ privateLayout.setHeadsUpInflatedSmartReplies(null);
+ }
+ setRepliesAndActions = true;
+ }
- if (AsyncHybridViewInflation.isEnabled()
- && (reInflateFlags & FLAG_CONTENT_VIEW_SINGLE_LINE) != 0) {
- HybridNotificationView viewHolder = result.mInflatedSingleLineViewHolder;
- SingleLineViewModel viewModel = result.mInflatedSingleLineViewModel;
- if (viewHolder != null && viewModel != null) {
- if (viewModel.isConversation()) {
- SingleLineConversationViewBinder.bind(
- result.mInflatedSingleLineViewModel,
- result.mInflatedSingleLineViewHolder
- );
- } else {
- SingleLineViewBinder.bind(result.mInflatedSingleLineViewModel,
- result.mInflatedSingleLineViewHolder);
- }
- privateLayout.setSingleLineView(result.mInflatedSingleLineViewHolder);
+ if (AsyncHybridViewInflation.isEnabled()
+ && (reInflateFlags & FLAG_CONTENT_VIEW_SINGLE_LINE) != 0) {
+ HybridNotificationView viewHolder = result.mInflatedSingleLineViewHolder;
+ SingleLineViewModel viewModel = result.mInflatedSingleLineViewModel;
+ if (viewHolder != null && viewModel != null) {
+ if (viewModel.isConversation()) {
+ SingleLineConversationViewBinder.bind(
+ result.mInflatedSingleLineViewModel,
+ result.mInflatedSingleLineViewHolder
+ );
+ } else {
+ SingleLineViewBinder.bind(result.mInflatedSingleLineViewModel,
+ result.mInflatedSingleLineViewHolder);
}
+ privateLayout.setSingleLineView(result.mInflatedSingleLineViewHolder);
}
+ }
- if (setRepliesAndActions) {
- privateLayout.setInflatedSmartReplyState(result.inflatedSmartReplyState);
+ if (setRepliesAndActions) {
+ privateLayout.setInflatedSmartReplyState(result.inflatedSmartReplyState);
+ }
+
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
+ if (result.inflatedPublicView != null) {
+ publicLayout.setContractedChild(result.inflatedPublicView);
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
+ result.newPublicView);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
+ remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
+ result.newPublicView);
}
+ }
- if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
- if (result.inflatedPublicView != null) {
- publicLayout.setContractedChild(result.inflatedPublicView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
- result.newPublicView);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
- result.newPublicView);
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ if ((reInflateFlags & FLAG_GROUP_SUMMARY_HEADER) != 0) {
+ if (result.mInflatedGroupHeaderView != null) {
+ row.setIsLowPriority(false);
+ row.setGroupHeader(/* headerView= */ result.mInflatedGroupHeaderView);
+ remoteViewCache.putCachedView(entry, FLAG_GROUP_SUMMARY_HEADER,
+ result.mNewGroupHeaderView);
+ } else if (remoteViewCache.hasCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)) {
+ // Re-inflation case. Only update if it's still cached (i.e. view has not
+ // been freed while inflating).
+ remoteViewCache.putCachedView(entry, FLAG_GROUP_SUMMARY_HEADER,
+ result.mNewGroupHeaderView);
}
}
- entry.headsUpStatusBarText = result.headsUpStatusBarText;
- entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
- if (endListener != null) {
- endListener.onAsyncInflationFinished(entry);
+ if ((reInflateFlags & FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER) != 0) {
+ if (result.mInflatedLowPriorityGroupHeaderView != null) {
+ // New view case, set row to low priority
+ row.setIsLowPriority(true);
+ row.setLowPriorityGroupHeader(
+ /* headerView= */ result.mInflatedLowPriorityGroupHeaderView);
+ remoteViewCache.putCachedView(entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+ result.mNewLowPriorityGroupHeaderView);
+ } else if (remoteViewCache.hasCachedView(entry,
+ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)) {
+ // Re-inflation case. Only update if it's still cached (i.e. view has not
+ // been freed while inflating).
+ remoteViewCache.putCachedView(entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+ result.mNewGroupHeaderView);
+ }
}
- return true;
}
- return false;
+
+ entry.headsUpStatusBarText = result.headsUpStatusBarText;
+ entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
+ if (endListener != null) {
+ endListener.onAsyncInflationFinished(entry);
+ }
+ return true;
}
private static RemoteViews createExpandedView(Notification.Builder builder,
@@ -1147,6 +1264,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private RemoteViews newHeadsUpView;
private RemoteViews newExpandedView;
private RemoteViews newPublicView;
+ private RemoteViews mNewGroupHeaderView;
+ private RemoteViews mNewLowPriorityGroupHeaderView;
@VisibleForTesting
Context packageContext;
@@ -1155,6 +1274,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private View inflatedHeadsUpView;
private View inflatedExpandedView;
private View inflatedPublicView;
+ private NotificationHeaderView mInflatedGroupHeaderView;
+ private NotificationHeaderView mInflatedLowPriorityGroupHeaderView;
private CharSequence headsUpStatusBarText;
private CharSequence headsUpStatusBarTextPublic;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt
index ee9462c60674..15c705579bf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt
@@ -27,6 +27,8 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import javax.inject.Inject
@@ -145,6 +147,12 @@ constructor(@NotifInflationLog private val buffer: LogBuffer) {
if (flag and FLAG_CONTENT_VIEW_SINGLE_LINE != 0) {
l.add("SINGLE_LINE")
}
+ if (flag and FLAG_GROUP_SUMMARY_HEADER != 0) {
+ l.add("GROUP_SUMMARY_HEADER")
+ }
+ if (flag and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) {
+ l.add("LOW_PRIORITY_GROUP_SUMMARY_HEADER")
+ }
return l.joinToString("|")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 374248252d1f..137e1b2ab809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -70,6 +70,7 @@ import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt;
import com.android.systemui.statusbar.policy.SmartReplyView;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.Compile;
+import com.android.systemui.util.DumpUtilsKt;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -97,6 +98,8 @@ public class NotificationContentView extends FrameLayout implements Notification
private static final int UNDEFINED = -1;
+ protected static final boolean INCLUDE_HEIGHTS_TO_DUMP = true;
+
private final Rect mClipBounds = new Rect();
private int mMinContractedHeight;
@@ -238,7 +241,10 @@ public class NotificationContentView extends FrameLayout implements Notification
mMinContractedHeight = getResources().getDimensionPixelSize(
R.dimen.min_notification_layout_height);
if (AsyncHybridViewInflation.isEnabled()) {
- //TODO: set the height with a more reasonable min single-line height
+ //TODO (b/217799515): single-line view height is the greater of two heights: text view
+ // height and icon height (when there's an icon). icon height is fixed to be
+ // conversation_single_line_face_pile_size (24dp), the text view's height is 16sp,
+ // its pixel height changes with the system's font scaling factor.
mMinSingleLineHeight = getResources().getDimensionPixelSize(
R.dimen.conversation_single_line_face_pile_size);
}
@@ -843,7 +849,7 @@ public class NotificationContentView extends FrameLayout implements Notification
if (mSingleLineView != null) {
return getViewHeight(VISIBLE_TYPE_SINGLELINE);
} else {
- Log.wtf(TAG, "getMinHeight: mSingleLineView == null");
+ //TODO(b/217799515): investigate the impact of min-height value
return mMinSingleLineHeight;
}
} else {
@@ -2193,6 +2199,11 @@ public class NotificationContentView extends FrameLayout implements Notification
pw.print("null");
}
pw.println();
+
+ if (INCLUDE_HEIGHTS_TO_DUMP) {
+ dumpContentDimensions(DumpUtilsKt.asIndenting(pw));
+ }
+
pw.println("mBubblesEnabledForUser: " + mBubblesEnabledForUser);
pw.print("RemoteInputViews { ");
@@ -2213,6 +2224,69 @@ public class NotificationContentView extends FrameLayout implements Notification
pw.println(" }");
}
+ private String visibleTypeToString(int visibleType) {
+ return switch (visibleType) {
+ case VISIBLE_TYPE_CONTRACTED -> "CONTRACTED";
+ case VISIBLE_TYPE_EXPANDED -> "EXPANDED";
+ case VISIBLE_TYPE_HEADSUP -> "HEADSUP";
+ case VISIBLE_TYPE_SINGLELINE -> "SINGLELINE";
+ default -> "NONE";
+ };
+ }
+
+ /** Add content views to dump */
+ private void dumpContentDimensions(IndentingPrintWriter pw) {
+ pw.print("ContentDimensions: ");
+ pw.print("visibleType(String)", visibleTypeToString(mVisibleType));
+ pw.print("measured width", getMeasuredWidth());
+ pw.print("measured height", getMeasuredHeight());
+ pw.print("maxHeight", getMaxHeight());
+ pw.print("minHeight", getMinHeight());
+ pw.println();
+ pw.println("ChildViews:");
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ final View contractedChild = mContractedChild;
+ if (contractedChild != null) {
+ dumpChildViewDimensions(pw, contractedChild, "Contracted Child:");
+ pw.println();
+ }
+
+ final View expandedChild = mExpandedChild;
+ if (expandedChild != null) {
+ dumpChildViewDimensions(pw, expandedChild, "Expanded Child:");
+ pw.println();
+ }
+
+ final View headsUpChild = mHeadsUpChild;
+ if (headsUpChild != null) {
+ dumpChildViewDimensions(pw, headsUpChild, "HeadsUp Child:");
+ pw.println();
+ }
+ final View singleLineView = mSingleLineView;
+ if (singleLineView != null) {
+ dumpChildViewDimensions(pw, singleLineView, "Single Line View:");
+ pw.println();
+ }
+
+ });
+ final int expandedRemoteInputHeight = getExtraRemoteInputHeight(mExpandedRemoteInput);
+ final int headsUpRemoteInputHeight = getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+ pw.print("expandedRemoteInputHeight", expandedRemoteInputHeight);
+ pw.print("headsUpRemoteInputHeight", headsUpRemoteInputHeight);
+ pw.println();
+ }
+
+ private void dumpChildViewDimensions(IndentingPrintWriter pw, View view,
+ String name) {
+ pw.print(name + " ");
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ pw.print("width", view.getWidth());
+ pw.print("height", view.getHeight());
+ pw.print("measuredWidth", view.getMeasuredWidth());
+ pw.print("measuredHeight", view.getMeasuredHeight());
+ });
+ }
+
/** Add any existing SmartReplyView to the dump */
public void dumpSmartReplies(IndentingPrintWriter pw) {
if (mHeadsUpSmartReplyView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 736140c44dfd..b0fd47587782 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -81,6 +81,8 @@ public interface NotificationRowContentBinder {
FLAG_CONTENT_VIEW_HEADS_UP,
FLAG_CONTENT_VIEW_PUBLIC,
FLAG_CONTENT_VIEW_SINGLE_LINE,
+ FLAG_GROUP_SUMMARY_HEADER,
+ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
FLAG_CONTENT_VIEW_ALL})
@interface InflationFlag {}
/**
@@ -108,7 +110,17 @@ public interface NotificationRowContentBinder {
*/
int FLAG_CONTENT_VIEW_SINGLE_LINE = 1 << 4;
- int FLAG_CONTENT_VIEW_ALL = (1 << 5) - 1;
+ /**
+ * The notification group summary header view
+ */
+ int FLAG_GROUP_SUMMARY_HEADER = 1 << 5;
+
+ /**
+ * The notification low-priority group summary header view
+ */
+ int FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER = 1 << 6;
+
+ int FLAG_CONTENT_VIEW_ALL = (1 << 7) - 1;
/**
* Parameters for content view binding
@@ -129,6 +141,11 @@ public interface NotificationRowContentBinder {
* Use increased height when binding heads up views.
*/
public boolean usesIncreasedHeadsUpHeight;
+
+ /**
+ * Is group summary notification
+ */
+ public boolean mIsGroupSummary;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationViewFlipperFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationViewFlipperFactory.kt
new file mode 100644
index 000000000000..0594c1227a44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationViewFlipperFactory.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.widget.ViewFlipper
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.statusbar.notification.row.ui.viewbinder.NotificationViewFlipperBinder
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.NotificationViewFlipperViewModel
+import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing
+import javax.inject.Inject
+
+/**
+ * A factory which owns the construction of any ViewFlipper inside of Notifications, and binds it
+ * with a view model. This ensures that ViewFlippers are paused when the keyguard is showing.
+ */
+class NotificationViewFlipperFactory
+@Inject
+constructor(
+ private val viewModel: NotificationViewFlipperViewModel,
+) : NotifRemoteViewsFactory {
+ init {
+ /* check if */ NotificationViewFlipperPausing.isUnexpectedlyInLegacyMode()
+ }
+
+ override fun instantiate(
+ row: ExpandableNotificationRow,
+ @InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View? {
+ return when (name) {
+ ViewFlipper::class.java.name,
+ ViewFlipper::class.java.simpleName ->
+ ViewFlipper(context, attrs).also { viewFlipper ->
+ NotificationViewFlipperBinder.bindWhileAttached(viewFlipper, viewModel)
+ }
+ else -> null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 9445d56ab509..ea3036e35c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -31,6 +31,7 @@ import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.time.SystemClock;
import javax.inject.Inject;
@@ -46,9 +47,14 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf
private NotificationEntry mEntry;
private boolean mCancelled;
private Throwable mInflateOrigin;
+ private final SystemClock mSystemClock;
+ private final RowInflaterTaskLogger mLogger;
+ private long mInflateStartTimeMs;
@Inject
- public RowInflaterTask() {
+ public RowInflaterTask(SystemClock systemClock, RowInflaterTaskLogger logger) {
+ mSystemClock = systemClock;
+ mLogger = logger;
}
/**
@@ -61,29 +67,49 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf
}
mListener = listener;
AsyncLayoutInflater inflater = com.android.systemui.Flags.notificationRowUserContext()
- ? new AsyncLayoutInflater(context, new RowAsyncLayoutInflater(entry))
+ ? new AsyncLayoutInflater(context, makeRowInflater(entry))
: new AsyncLayoutInflater(context);
mEntry = entry;
entry.setInflationTask(this);
+
+ mLogger.logInflateStart(entry);
+ mInflateStartTimeMs = mSystemClock.elapsedRealtime();
inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}
+ private RowAsyncLayoutInflater makeRowInflater(NotificationEntry entry) {
+ return new RowAsyncLayoutInflater(entry, mSystemClock, mLogger);
+ }
+
@VisibleForTesting
static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
private final NotificationEntry mEntry;
+ private final SystemClock mSystemClock;
+ private final RowInflaterTaskLogger mLogger;
- RowAsyncLayoutInflater(NotificationEntry entry) {
+ RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock,
+ RowInflaterTaskLogger logger) {
mEntry = entry;
+ mSystemClock = systemClock;
+ mLogger = logger;
}
@Nullable
@Override
public View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs) {
- if (name.equals(ExpandableNotificationRow.class.getName())) {
- return new ExpandableNotificationRow(context, attrs, mEntry);
+ if (!name.equals(ExpandableNotificationRow.class.getName())) {
+ return null;
}
- return null;
+
+ final long startMs = mSystemClock.elapsedRealtime();
+ final ExpandableNotificationRow row =
+ new ExpandableNotificationRow(context, attrs, mEntry);
+ final long elapsedMs = mSystemClock.elapsedRealtime() - startMs;
+
+ mLogger.logCreatedRow(mEntry, elapsedMs);
+
+ return row;
}
@Nullable
@@ -101,6 +127,9 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf
@Override
public void onInflateFinished(View view, int resid, ViewGroup parent) {
+ final long elapsedMs = mSystemClock.elapsedRealtime() - mInflateStartTimeMs;
+ mLogger.logInflateFinish(mEntry, elapsedMs, mCancelled);
+
if (!mCancelled) {
try {
mEntry.onInflationTaskFinished();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTaskLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTaskLogger.kt
new file mode 100644
index 000000000000..94da6bec799b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTaskLogger.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.NotifInflationLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
+import javax.inject.Inject
+
+class RowInflaterTaskLogger @Inject constructor(@NotifInflationLog private val buffer: LogBuffer) {
+ fun logInflateStart(entry: NotificationEntry) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = entry.logKey },
+ { "started row inflation for $str1" }
+ )
+ }
+
+ fun logCreatedRow(entry: NotificationEntry, elapsedMs: Long) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = entry.logKey
+ long1 = elapsedMs
+ },
+ { "created row in $long1 ms for $str1" }
+ )
+ }
+
+ fun logInflateFinish(entry: NotificationEntry, elapsedMs: Long, cancelled: Boolean) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = entry.logKey
+ long1 = elapsedMs
+ bool1 = cancelled
+ },
+ { "finished ${if (bool1) "cancelled " else ""}row inflation in $long1 ms for $str1" }
+ )
+ }
+}
+
+private const val TAG = "RowInflaterTask"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt
new file mode 100644
index 000000000000..133d3e730eff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.viewbinder
+
+import android.widget.ViewFlipper
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.NotificationViewFlipperViewModel
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/** Binds a [NotificationViewFlipper] to its [view model][NotificationViewFlipperViewModel]. */
+object NotificationViewFlipperBinder {
+ fun bindWhileAttached(
+ viewFlipper: ViewFlipper,
+ viewModel: NotificationViewFlipperViewModel,
+ ): DisposableHandle {
+ if (viewFlipper.isAutoStart) {
+ // If the ViewFlipper is not set to AutoStart, the pause binding is meaningless
+ return DisposableHandle {}
+ }
+ return viewFlipper.repeatWhenAttached {
+ lifecycleScope.launch { bind(viewFlipper, viewModel) }
+ }
+ }
+
+ suspend fun bind(
+ viewFlipper: ViewFlipper,
+ viewModel: NotificationViewFlipperViewModel,
+ ) = coroutineScope { launch { viewModel.isPaused.collect { viewFlipper.setPaused(it) } } }
+
+ private fun ViewFlipper.setPaused(paused: Boolean) {
+ if (paused) {
+ stopFlipping()
+ } else if (isAutoStart) {
+ startFlipping()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModel.kt
new file mode 100644
index 000000000000..7694e58296ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModel.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import javax.inject.Inject
+
+/** A model which represents whether ViewFlippers inside notifications should be paused. */
+@SysUISingleton
+class NotificationViewFlipperViewModel
+@Inject
+constructor(
+ dumpManager: DumpManager,
+ stackInteractor: NotificationStackInteractor,
+) : FlowDumperImpl(dumpManager) {
+ init {
+ /* check if */ NotificationViewFlipperPausing.isUnexpectedlyInLegacyMode()
+ }
+
+ val isPaused = stackInteractor.isShowingOnLockscreen.dumpWhileCollecting("isPaused")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationContentAlphaOptimization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationContentAlphaOptimization.kt
new file mode 100644
index 000000000000..c70359533eba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationContentAlphaOptimization.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the Async Group Header Inflation flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationContentAlphaOptimization {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_CONTENT_ALPHA_OPTIMIZATION
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the async inflation of group header views enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationContentAlphaOptimization()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationViewFlipperPausing.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationViewFlipperPausing.kt
new file mode 100644
index 000000000000..cea6a2b197be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationViewFlipperPausing.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the notification view flipper pausing flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationViewFlipperPausing {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_VIEW_FLIPPER_PAUSING
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationViewFlipperPausing()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index abf6c27c68ac..fa973001cec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -55,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.HybridGroupManager;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -131,6 +132,7 @@ public class NotificationChildrenContainer extends ViewGroup
private int mUntruncatedChildCount;
private boolean mContainingNotificationIsFaded = false;
private RoundableState mRoundableState;
+ private int mMinSingleLineHeight;
private NotificationChildrenContainerLogger mLogger;
@@ -183,6 +185,8 @@ public class NotificationChildrenContainer extends ViewGroup
com.android.internal.R.dimen.notification_content_margin)
- mNotificationHeaderMargin;
mHybridGroupManager.initDimens();
+ mMinSingleLineHeight = getResources().getDimensionPixelSize(
+ R.dimen.conversation_single_line_face_pile_size);
}
@NonNull
@@ -385,16 +389,31 @@ public class NotificationChildrenContainer extends ViewGroup
return mAttachedChildren.size();
}
- public void recreateNotificationHeader(OnClickListener listener, boolean isConversation) {
+ /**
+ * Re-create the Notification header view
+ * @param listener OnClickListener of the header view
+ * @param isConversation if the notification group is a conversation group
+ */
+ public void recreateNotificationHeader(
+ OnClickListener listener,
+ boolean isConversation
+ ) {
+ // We don't want to inflate headers from the main thread when async inflation enabled
+ AsyncGroupHeaderViewInflation.assertInLegacyMode();
+ // TODO(b/217799515): remove traces from this function in a follow-up change
Trace.beginSection("NotifChildCont#recreateHeader");
mHeaderClickListener = listener;
mIsConversation = isConversation;
StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
notification.getNotification());
+ Trace.beginSection("recreateHeader#makeNotificationGroupHeader");
RemoteViews header = builder.makeNotificationGroupHeader();
+ Trace.endSection();
if (mNotificationHeader == null) {
+ Trace.beginSection("recreateHeader#apply");
mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
+ Trace.endSection();
mNotificationHeader.findViewById(com.android.internal.R.id.expand_button)
.setVisibility(VISIBLE);
mNotificationHeader.setOnClickListener(mHeaderClickListener);
@@ -407,7 +426,9 @@ public class NotificationChildrenContainer extends ViewGroup
addView(mNotificationHeader, 0);
invalidate();
} else {
+ Trace.beginSection("recreateHeader#reapply");
header.reapply(getContext(), mNotificationHeader);
+ Trace.endSection();
}
mNotificationHeaderWrapper.setExpanded(mChildrenExpanded);
mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
@@ -417,12 +438,105 @@ public class NotificationChildrenContainer extends ViewGroup
Trace.endSection();
}
+ private void removeGroupHeader() {
+ if (mNotificationHeader == null) {
+ return;
+ }
+ removeView(mNotificationHeader);
+ mNotificationHeader = null;
+ mNotificationHeaderWrapper = null;
+ }
+
+ private void removeLowPriorityGroupHeader() {
+ if (mNotificationHeaderLowPriority == null) {
+ return;
+ }
+ removeView(mNotificationHeaderLowPriority);
+ mNotificationHeaderLowPriority = null;
+ mNotificationHeaderWrapperLowPriority = null;
+ }
+
+ /**
+ * Set the group header view
+ * @param headerView view to set
+ * @param onClickListener OnClickListener of the header view
+ */
+ public void setGroupHeader(
+ NotificationHeaderView headerView,
+ OnClickListener onClickListener
+ ) {
+ if (AsyncGroupHeaderViewInflation.isUnexpectedlyInLegacyMode()) return;
+ mHeaderClickListener = onClickListener;
+
+ removeGroupHeader();
+
+ if (headerView == null) {
+ return;
+ }
+
+ mNotificationHeader = headerView;
+ mNotificationHeader.findViewById(com.android.internal.R.id.expand_button)
+ .setVisibility(VISIBLE);
+ mNotificationHeader.setOnClickListener(mHeaderClickListener);
+ mNotificationHeaderWrapper =
+ (NotificationHeaderViewWrapper) NotificationViewWrapper.wrap(
+ getContext(),
+ mNotificationHeader,
+ mContainingNotification);
+ mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate);
+ addView(mNotificationHeader, 0);
+ invalidate();
+
+ mNotificationHeaderWrapper.setExpanded(mChildrenExpanded);
+ mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
+
+ updateHeaderVisibility(false /* animate */);
+ updateChildrenAppearance();
+
+ Trace.endSection();
+ }
+
+ /**
+ * Set the low-priority group header view
+ * @param headerViewLowPriority header view to set
+ * @param onClickListener OnClickListener of the header view
+ */
+ public void setLowPriorityGroupHeader(
+ NotificationHeaderView headerViewLowPriority,
+ OnClickListener onClickListener
+ ) {
+ if (AsyncGroupHeaderViewInflation.isUnexpectedlyInLegacyMode()) return;
+ removeLowPriorityGroupHeader();
+ if (headerViewLowPriority == null) {
+ return;
+ }
+
+ mNotificationHeaderLowPriority = headerViewLowPriority;
+ mNotificationHeaderLowPriority.findViewById(com.android.internal.R.id.expand_button)
+ .setVisibility(VISIBLE);
+ mNotificationHeaderLowPriority.setOnClickListener(onClickListener);
+ mNotificationHeaderWrapperLowPriority =
+ (NotificationHeaderViewWrapper) NotificationViewWrapper.wrap(
+ getContext(),
+ mNotificationHeaderLowPriority,
+ mContainingNotification);
+ mNotificationHeaderWrapperLowPriority.setOnRoundnessChangedListener(this::invalidate);
+ addView(mNotificationHeaderLowPriority, 0);
+ invalidate();
+
+ mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
+ updateHeaderVisibility(false /* animate */);
+ updateChildrenAppearance();
+ }
+
/**
* Recreate the low-priority header.
*
* @param builder a builder to reuse. Otherwise the builder will be recovered.
*/
- private void recreateLowPriorityHeader(Notification.Builder builder, boolean isConversation) {
+ @VisibleForTesting
+ void recreateLowPriorityHeader(Notification.Builder builder, boolean isConversation) {
+ AsyncGroupHeaderViewInflation.assertInLegacyMode();
RemoteViews header;
StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
if (mIsLowPriority) {
@@ -527,8 +641,10 @@ public class NotificationChildrenContainer extends ViewGroup
* @param alpha alpha value to apply to the content
*/
public void setContentAlpha(float alpha) {
- for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
- mNotificationHeader.getChildAt(i).setAlpha(alpha);
+ if (mNotificationHeader != null) {
+ for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
+ mNotificationHeader.getChildAt(i).setAlpha(alpha);
+ }
}
for (ExpandableNotificationRow child : getAttachedChildren()) {
child.setContentAlpha(alpha);
@@ -564,7 +680,11 @@ public class NotificationChildrenContainer extends ViewGroup
*/
private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
if (showingAsLowPriority()) {
- return mNotificationHeaderLowPriority.getHeight();
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ return mHeaderHeight;
+ } else {
+ return mNotificationHeaderLowPriority.getHeight();
+ }
}
int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
int visibleChildren = 0;
@@ -1023,6 +1143,14 @@ public class NotificationChildrenContainer extends ViewGroup
return mCurrentHeader;
}
+ public NotificationHeaderView getNotificationHeader() {
+ return mNotificationHeader;
+ }
+
+ public NotificationHeaderView getNotificationHeaderLowPriority() {
+ return mNotificationHeaderLowPriority;
+ }
+
private void updateHeaderVisibility(boolean animate) {
ViewGroup desiredHeader;
ViewGroup currentHeader = mCurrentHeader;
@@ -1032,6 +1160,10 @@ public class NotificationChildrenContainer extends ViewGroup
return;
}
+ if (AsyncGroupHeaderViewInflation.isEnabled() && desiredHeader == null) {
+ return;
+ }
+
if (animate) {
if (desiredHeader != null && currentHeader != null) {
currentHeader.setVisibility(VISIBLE);
@@ -1271,6 +1403,9 @@ public class NotificationChildrenContainer extends ViewGroup
boolean likeHighPriority,
int headerTranslation) {
if (!likeHighPriority && showingAsLowPriority()) {
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ return mHeaderHeight;
+ }
if (mNotificationHeaderLowPriority == null) {
Log.e(TAG, "getMinHeight: low priority header is null", new Exception());
return 0;
@@ -1295,8 +1430,12 @@ public class NotificationChildrenContainer extends ViewGroup
if (singleLineView != null) {
minExpandHeight += singleLineView.getHeight();
} else {
- Log.e(TAG, "getMinHeight: child " + child.getEntry().getKey()
- + " single line view is null", new Exception());
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ minExpandHeight += mMinSingleLineHeight;
+ } else {
+ Log.e(TAG, "getMinHeight: child " + child.getEntry().getKey()
+ + " single line view is null", new Exception());
+ }
}
visibleChildren++;
}
@@ -1309,15 +1448,19 @@ public class NotificationChildrenContainer extends ViewGroup
}
public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
- if (mNotificationHeader != null) {
- removeView(mNotificationHeader);
- mNotificationHeader = null;
- }
- if (mNotificationHeaderLowPriority != null) {
- removeView(mNotificationHeaderLowPriority);
- mNotificationHeaderLowPriority = null;
+ if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+ // When Async header inflation is enabled, we do not reinflate headers because they are
+ // inflated from the background thread
+ if (mNotificationHeader != null) {
+ removeView(mNotificationHeader);
+ mNotificationHeader = null;
+ }
+ if (mNotificationHeaderLowPriority != null) {
+ removeView(mNotificationHeaderLowPriority);
+ mNotificationHeaderLowPriority = null;
+ }
+ recreateNotificationHeader(listener, mIsConversation);
}
- recreateNotificationHeader(listener, mIsConversation);
initDimens();
for (int i = 0; i < mDividers.size(); i++) {
View prevDivider = mDividers.get(i);
@@ -1395,7 +1538,9 @@ public class NotificationChildrenContainer extends ViewGroup
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
- recreateLowPriorityHeader(null /* existingBuilder */, mIsConversation);
+ if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+ recreateLowPriorityHeader(null /* existingBuilder */, mIsConversation);
+ }
updateHeaderVisibility(false /* animate */);
}
if (mUserLocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e397a70ea1f2..27db84f6715e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -124,6 +124,7 @@ import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.ColorUtilKt;
+import com.android.systemui.util.Compile;
import com.android.systemui.util.DumpUtilsKt;
import com.google.errorprone.annotations.CompileTimeConstant;
@@ -150,6 +151,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
private static final boolean SPEW = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean DEBUG_UPDATE_SIDE_PADDING = Compile.IS_DEBUG;
private boolean mShadeNeedsToClose = false;
@@ -218,6 +220,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
int mBottomInset = 0;
private float mQsExpansionFraction;
private final int mSplitShadeMinContentHeight;
+ private String mLastUpdateSidePaddingDumpString;
/**
* The algorithm which calculates the properties for our children
@@ -316,6 +319,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
= new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
+ if (SceneContainerFlag.isEnabled() && !mChildrenUpdateRequested) {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return true;
+ }
updateForcedScroll();
updateChildren();
mChildrenUpdateRequested = false;
@@ -677,7 +684,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
res.getBoolean(R.bool.config_drawNotificationBackground);
setOutlineProvider(mOutlineProvider);
- boolean willDraw = mShouldDrawNotificationBackground || mDebugLines;
+ // We could set this whenever we 'requestChildUpdate' much like the viewTreeObserver, but
+ // that adds a bunch of complexity, and drawing nothing isn't *that* expensive.
+ boolean willDraw = SceneContainerFlag.isEnabled()
+ || mShouldDrawNotificationBackground || mDebugLines;
setWillNotDraw(!willDraw);
mBackgroundPaint.setAntiAlias(true);
if (mDebugLines) {
@@ -813,7 +823,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
+ private void onJustBeforeDraw() {
+ if (SceneContainerFlag.isEnabled()) {
+ if (mChildrenUpdateRequested) {
+ updateForcedScroll();
+ updateChildren();
+ mChildrenUpdateRequested = false;
+ }
+ }
+ }
+
protected void onDraw(Canvas canvas) {
+ onJustBeforeDraw();
if (mShouldDrawNotificationBackground
&& (mSections[0].getCurrentBounds().top
< mSections[mSections.length - 1].getCurrentBounds().bottom
@@ -1106,15 +1127,28 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
void updateSidePadding(int viewWidth) {
+ final boolean portrait =
+ getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+
+ mLastUpdateSidePaddingDumpString = "viewWidth=" + viewWidth
+ + " skinnyNotifsInLandscape=" + mSkinnyNotifsInLandscape
+ + " portrait=" + portrait;
+
+ if (DEBUG_UPDATE_SIDE_PADDING) {
+ Log.v(TAG, "updateSidePadding: " + mLastUpdateSidePaddingDumpString);
+ }
+
if (viewWidth == 0 || !mSkinnyNotifsInLandscape) {
mSidePaddings = mMinimumPaddings;
return;
}
+
// Portrait is easy, just use the dimen for paddings
- if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (portrait) {
mSidePaddings = mMinimumPaddings;
return;
}
+
final int innerWidth = viewWidth - mMinimumPaddings * 2;
final int qsTileWidth = (innerWidth - mQsTilePadding * 3) / 4;
mSidePaddings = mMinimumPaddings + qsTileWidth + mQsTilePadding;
@@ -4992,6 +5026,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
+ generateHeadsUpAnimation(row, isHeadsUp);
+ }
+
+ /**
+ * Notifies the NSSL, that the given view would need a HeadsUp animation, when it is being
+ * added to this container.
+ *
+ * @param row to animate
+ * @param isHeadsUp true for appear, false for disappear animations
+ */
+ public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
final boolean add = mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed);
if (SPEW) {
Log.v(TAG, "generateHeadsUpAnimation:"
@@ -5294,6 +5339,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
println(pw, "translationX", getTranslationX());
println(pw, "translationY", getTranslationY());
println(pw, "translationZ", getTranslationZ());
+ println(pw, "skinnyNotifsInLandscape", mSkinnyNotifsInLandscape);
+ println(pw, "minimumPaddings", mMinimumPaddings);
+ println(pw, "qsTilePadding", mQsTilePadding);
+ println(pw, "sidePaddings", mSidePaddings);
+ println(pw, "lastUpdateSidePadding", mLastUpdateSidePaddingDumpString);
mNotificationStackSizeCalculator.dump(pw, args);
});
pw.println();
@@ -5725,11 +5775,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mShelf.updateAppearance();
}
- void setTopHeadsUpRow(ExpandableNotificationRow topHeadsUpRow) {
+ /**
+ * @param topHeadsUpRow the first headsUp row in z-order.
+ */
+ public void setTopHeadsUpRow(ExpandableNotificationRow topHeadsUpRow) {
mTopHeadsUpRow = topHeadsUpRow;
}
- void setNumHeadsUp(long numHeadsUp) {
+ /**
+ * @param numHeadsUp the number of active alerting notifications.
+ */
+ public void setNumHeadsUp(long numHeadsUp) {
mNumHeadsUp = numHeadsUp;
mAmbientState.setHasHeadsUpEntries(numHeadsUp > 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractor.kt
new file mode 100644
index 000000000000..9cd46f5b3552
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractor.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/** Interactor exposing states related to the stack's context */
+@SysUISingleton
+class NotificationStackInteractor
+@Inject
+constructor(
+ keyguardInteractor: KeyguardInteractor,
+ powerInteractor: PowerInteractor,
+) {
+ val isShowingOnLockscreen: Flow<Boolean> =
+ combine(
+ // Non-notification UI elements of the notification list should not be visible
+ // on the lockscreen (incl. AOD and bouncer), except if the shade is opened on
+ // top. See b/219680200 for the footer and b/228790482, b/267060171 for the
+ // empty shade.
+ // TODO(b/323187006): There's a plan to eventually get rid of StatusBarState
+ // entirely, so this will have to be replaced at some point.
+ keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD },
+ // The StatusBarState is unfortunately not updated quickly enough when the power
+ // button is pressed, so this is necessary in addition to the KEYGUARD check to
+ // cover the transition to AOD while going to sleep (b/190227875).
+ powerInteractor.isAsleep,
+ ) { (isOnKeyguard, isAsleep) ->
+ isOnKeyguard || isAsleep
+ }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 7b502564ecb1..c85a18a8a896 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -26,6 +23,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.SeenNotific
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.kotlin.combine
@@ -51,8 +49,7 @@ constructor(
val footer: Optional<FooterViewModel>,
val logger: Optional<NotificationLoggerViewModel>,
activeNotificationsInteractor: ActiveNotificationsInteractor,
- keyguardInteractor: KeyguardInteractor,
- powerInteractor: PowerInteractor,
+ notificationStackInteractor: NotificationStackInteractor,
remoteInputInteractor: RemoteInputInteractor,
seenNotificationsInteractor: SeenNotificationsInteractor,
shadeInteractor: ShadeInteractor,
@@ -71,7 +68,7 @@ constructor(
} else {
combine(
activeNotificationsInteractor.areAnyNotificationsPresent,
- isShowingOnLockscreen,
+ notificationStackInteractor.isShowingOnLockscreen,
) { hasNotifications, isShowingOnLockscreen ->
hasNotifications || !isShowingOnLockscreen
}
@@ -86,7 +83,7 @@ constructor(
combine(
activeNotificationsInteractor.areAnyNotificationsPresent,
shadeInteractor.isQsFullscreen,
- isShowingOnLockscreen,
+ notificationStackInteractor.isShowingOnLockscreen,
) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
when {
hasNotifications -> false
@@ -109,7 +106,7 @@ constructor(
combine(
activeNotificationsInteractor.areAnyNotificationsPresent,
userSetupInteractor.isUserSetUp,
- isShowingOnLockscreen,
+ notificationStackInteractor.isShowingOnLockscreen,
shadeInteractor.qsExpansion,
shadeInteractor.isQsFullscreen,
remoteInputInteractor.isRemoteInputActive,
@@ -177,29 +174,6 @@ constructor(
SHOW_WITH_ANIMATION(visible = true, canAnimate = true)
}
- private val isShowingOnLockscreen: Flow<Boolean> by lazy {
- if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
- flowOf(false)
- } else {
- combine(
- // Non-notification UI elements of the notification list should not be visible
- // on the lockscreen (incl. AOD and bouncer), except if the shade is opened on
- // top. See b/219680200 for the footer and b/228790482, b/267060171 for the
- // empty shade.
- // TODO(b/323187006): There's a plan to eventually get rid of StatusBarState
- // entirely, so this will have to be replaced at some point.
- keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD },
- // The StatusBarState is unfortunately not updated quickly enough when the power
- // button is pressed, so this is necessary in addition to the KEYGUARD check to
- // cover the transition to AOD while going to sleep (b/190227875).
- powerInteractor.isAsleep,
- ) { (isOnKeyguard, isAsleep) ->
- isOnKeyguard || isAsleep
- }
- .distinctUntilChanged()
- }
- }
-
// TODO(b/308591475): This should be tracked separately by the empty shade.
val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
index 3a0f03f70e1c..8d1cdfac90c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -17,13 +17,15 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -35,10 +37,11 @@ import kotlinx.coroutines.flow.distinctUntilChanged
class NotificationStackAppearanceViewModel
@Inject
constructor(
+ dumpManager: DumpManager,
stackAppearanceInteractor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
sceneInteractor: SceneInteractor,
-) {
+) : FlowDumperImpl(dumpManager) {
/**
* The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
* from Gone to Shade scenes, and remain at 1 when in Lockscreen or Shade scenes and while
@@ -51,7 +54,7 @@ constructor(
) { shadeExpansion, transitionState ->
when (transitionState) {
is ObservableTransitionState.Idle -> {
- if (transitionState.scene == SceneKey.Lockscreen) {
+ if (transitionState.scene == Scenes.Lockscreen) {
1f
} else {
shadeExpansion
@@ -59,10 +62,10 @@ constructor(
}
is ObservableTransitionState.Transition -> {
if (
- (transitionState.fromScene == SceneKey.Shade &&
- transitionState.toScene == SceneKey.QuickSettings) ||
- (transitionState.fromScene == SceneKey.QuickSettings &&
- transitionState.toScene == SceneKey.Shade)
+ (transitionState.fromScene == Scenes.Shade &&
+ transitionState.toScene == Scenes.QuickSettings) ||
+ (transitionState.fromScene == Scenes.QuickSettings &&
+ transitionState.toScene == Scenes.Shade)
) {
1f
} else {
@@ -72,10 +75,12 @@ constructor(
}
}
.distinctUntilChanged()
+ .dumpWhileCollecting("expandFraction")
/** The bounds of the notification stack in the current scene. */
- val stackBounds: Flow<NotificationContainerBounds> = stackAppearanceInteractor.stackBounds
+ val stackBounds: Flow<NotificationContainerBounds> =
+ stackAppearanceInteractor.stackBounds.dumpValue("stackBounds")
/** The y-coordinate in px of top of the contents of the notification stack. */
- val contentTop: StateFlow<Float> = stackAppearanceInteractor.contentTop
+ val contentTop: StateFlow<Float> = stackAppearanceInteractor.contentTop.dumpValue("contentTop")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index b4c88c5f9e79..78fc1471053d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -41,8 +42,10 @@ import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
@@ -60,6 +63,7 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTran
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -88,6 +92,7 @@ class SharedNotificationContainerViewModel
@Inject
constructor(
private val interactor: SharedNotificationContainerInteractor,
+ dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -96,7 +101,9 @@ constructor(
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
+ private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel,
private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
@@ -116,7 +123,7 @@ constructor(
private val primaryBouncerToLockscreenTransitionViewModel:
PrimaryBouncerToLockscreenTransitionViewModel,
private val aodBurnInViewModel: AodBurnInViewModel,
-) {
+) : FlowDumperImpl(dumpManager) {
private val statesForConstrainedNotifications: Set<KeyguardState> =
setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER)
@@ -126,6 +133,7 @@ constructor(
.map { it.transitionState == STARTED || it.transitionState == RUNNING }
.distinctUntilChanged()
.onStart { emit(false) }
+ .dumpWhileCollecting("lockscreenToGlanceableHubRunning")
private val glanceableHubToLockscreenRunning =
keyguardTransitionInteractor
@@ -133,6 +141,7 @@ constructor(
.map { it.transitionState == STARTED || it.transitionState == RUNNING }
.distinctUntilChanged()
.onStart { emit(false) }
+ .dumpWhileCollecting("glanceableHubToLockscreenRunning")
/**
* Shade locked is a legacy concept, but necessary to mimic current functionality. Listen for
@@ -148,8 +157,10 @@ constructor(
isShadeLocked && (isQsExpanded || isShadeExpanded)
}
.distinctUntilChanged()
+ .dumpWhileCollecting("isShadeLocked")
- val shadeCollapseFadeInComplete = MutableStateFlow(false)
+ private val shadeCollapseFadeInComplete =
+ MutableStateFlow(false).dumpValue("shadeCollapseFadeInComplete")
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
interactor.configurationBasedDimensions
@@ -171,6 +182,7 @@ constructor(
)
}
.distinctUntilChanged()
+ .dumpWhileCollecting("configurationBasedDimensions")
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
@@ -179,13 +191,13 @@ constructor(
statesForConstrainedNotifications.contains(it)
},
keyguardTransitionInteractor
- .transitionValue(LOCKSCREEN)
- .onStart { emit(0f) }
- .map { it > 0 }
+ .isInTransitionWhere { from, to -> from == LOCKSCREEN || to == LOCKSCREEN }
+ .onStart { emit(false) }
) { constrainedNotificationState, transitioningToOrFromLockscreen ->
constrainedNotificationState || transitioningToOrFromLockscreen
}
.distinctUntilChanged()
+ .dumpWhileCollecting("isOnLockscreen")
/** Are we purely on the keyguard without the shade/qs? */
val isOnLockscreenWithoutShade: Flow<Boolean> =
@@ -204,6 +216,7 @@ constructor(
started = SharingStarted.Eagerly,
initialValue = false,
)
+ .dumpValue("isOnLockscreenWithoutShade")
/** Are we purely on the glanceable hub without the shade/qs? */
val isOnGlanceableHubWithoutShade: Flow<Boolean> =
@@ -222,6 +235,7 @@ constructor(
started = SharingStarted.WhileSubscribed(),
initialValue = false,
)
+ .dumpWhileCollecting("isOnGlanceableHubWithoutShade")
/**
* Fade in if the user swipes the shade back up, not if collapsed by going to AOD. This is
@@ -284,6 +298,7 @@ constructor(
started = SharingStarted.WhileSubscribed(),
initialValue = false,
)
+ .dumpWhileCollecting("shadeCollapseFadeIn")
/**
* The container occupies the entire screen, and must be positioned relative to other elements.
@@ -322,6 +337,7 @@ constructor(
started = SharingStarted.Lazily,
initialValue = NotificationContainerBounds(),
)
+ .dumpValue("bounds")
/**
* Ensure view is visible when the shade/qs are expanded. Also, as QS is expanding, fade out
@@ -345,18 +361,20 @@ constructor(
}
}
.onStart { emit(0f) }
+ .dumpWhileCollecting("alphaForShadeAndQsExpansion")
private val alphaWhenGoneAndShadeState: Flow<Float> =
combineTransform(
- keyguardTransitionInteractor.transitions
- .map { step -> step.to == GONE && step.transitionState == FINISHED }
- .distinctUntilChanged(),
- keyguardInteractor.statusBarState,
- ) { isGoneTransitionFinished, statusBarState ->
- if (isGoneTransitionFinished && statusBarState == SHADE) {
- emit(1f)
+ keyguardTransitionInteractor.transitions
+ .map { step -> step.to == GONE && step.transitionState == FINISHED }
+ .distinctUntilChanged(),
+ keyguardInteractor.statusBarState,
+ ) { isGoneTransitionFinished, statusBarState ->
+ if (isGoneTransitionFinished && statusBarState == SHADE) {
+ emit(1f)
+ }
}
- }
+ .dumpWhileCollecting("alphaWhenGoneAndShadeState")
fun expansionAlpha(viewState: ViewStateAccessor): Flow<Float> {
// All transition view models are mututally exclusive, and safe to merge
@@ -364,7 +382,9 @@ constructor(
merge(
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
aodToLockscreenTransitionViewModel.notificationAlpha,
+ aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
+ dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
goneToAodTransitionViewModel.notificationAlpha,
goneToDreamingTransitionViewModel.lockscreenAlpha,
@@ -389,7 +409,9 @@ constructor(
isOnLockscreenWithoutShade,
shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
- keyguardInteractor.dismissAlpha,
+ keyguardInteractor.dismissAlpha.dumpWhileCollecting(
+ "keyguardInteractor.keyguardAlpha"
+ ),
) {
isOnLockscreenWithoutShade,
shadeCollapseFadeIn,
@@ -405,6 +427,7 @@ constructor(
},
)
.distinctUntilChanged()
+ .dumpWhileCollecting("expansionAlpha")
}
/**
@@ -415,29 +438,35 @@ constructor(
* alpha sources.
*/
val glanceableHubAlpha: Flow<Float> =
- isOnGlanceableHubWithoutShade.flatMapLatest { isOnGlanceableHubWithoutShade ->
- combineTransform(
- lockscreenToGlanceableHubRunning,
- glanceableHubToLockscreenRunning,
- merge(
- lockscreenToGlanceableHubTransitionViewModel.notificationAlpha,
- glanceableHubToLockscreenTransitionViewModel.notificationAlpha,
- )
- ) { lockscreenToGlanceableHubRunning, glanceableHubToLockscreenRunning, alpha ->
- if (isOnGlanceableHubWithoutShade) {
- // Notifications should not be visible on the glanceable hub.
- // TODO(b/321075734): implement a way to actually set the notifications to gone
- // while on the hub instead of just adjusting alpha
- emit(0f)
- } else if (lockscreenToGlanceableHubRunning || glanceableHubToLockscreenRunning) {
- emit(alpha)
- } else {
- // Not on the hub and no transitions running, return full visibility so we don't
- // block the notifications from showing.
- emit(1f)
+ isOnGlanceableHubWithoutShade
+ .flatMapLatest { isOnGlanceableHubWithoutShade ->
+ combineTransform(
+ lockscreenToGlanceableHubRunning,
+ glanceableHubToLockscreenRunning,
+ merge(
+ lockscreenToGlanceableHubTransitionViewModel.notificationAlpha,
+ glanceableHubToLockscreenTransitionViewModel.notificationAlpha,
+ )
+ ) { lockscreenToGlanceableHubRunning, glanceableHubToLockscreenRunning, alpha ->
+ if (isOnGlanceableHubWithoutShade) {
+ // Notifications should not be visible on the glanceable hub.
+ // TODO(b/321075734): implement a way to actually set the notifications to
+ // gone
+ // while on the hub instead of just adjusting alpha
+ emit(0f)
+ } else if (
+ lockscreenToGlanceableHubRunning || glanceableHubToLockscreenRunning
+ ) {
+ emit(alpha)
+ } else {
+ // Not on the hub and no transitions running, return full visibility so we
+ // don't
+ // block the notifications from showing.
+ emit(1f)
+ }
}
}
- }
+ .dumpWhileCollecting("glanceableHubAlpha")
/**
* Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
@@ -445,19 +474,20 @@ constructor(
*/
fun translationY(params: BurnInParameters): Flow<Float> {
return combine(
- aodBurnInViewModel.translationY(params).onStart { emit(0f) },
- isOnLockscreenWithoutShade,
- merge(
- keyguardInteractor.keyguardTranslationY,
- occludedToLockscreenTransitionViewModel.lockscreenTranslationY,
- )
- ) { burnInY, isOnLockscreenWithoutShade, translationY ->
- if (isOnLockscreenWithoutShade) {
- burnInY + translationY
- } else {
- 0f
+ aodBurnInViewModel.translationY(params).onStart { emit(0f) },
+ isOnLockscreenWithoutShade,
+ merge(
+ keyguardInteractor.keyguardTranslationY,
+ occludedToLockscreenTransitionViewModel.lockscreenTranslationY,
+ )
+ ) { burnInY, isOnLockscreenWithoutShade, translationY ->
+ if (isOnLockscreenWithoutShade) {
+ burnInY + translationY
+ } else {
+ 0f
+ }
}
- }
+ .dumpWhileCollecting("translationY")
}
/**
@@ -466,9 +496,10 @@ constructor(
*/
val translationX: Flow<Float> =
merge(
- lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX,
- glanceableHubToLockscreenTransitionViewModel.notificationTranslationX,
- )
+ lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX,
+ glanceableHubToLockscreenTransitionViewModel.notificationTranslationX,
+ )
+ .dumpWhileCollecting("translationX")
/**
* When on keyguard, there is limited space to display notifications so calculate how many could
@@ -510,6 +541,7 @@ constructor(
}
}
.distinctUntilChanged()
+ .dumpWhileCollecting("maxNotifications")
}
fun notificationStackChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 270b94b1893e..23a080b7b931 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -45,8 +45,8 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -71,7 +71,7 @@ constructor(
private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val shadeControllerLazy: Lazy<ShadeController>,
- private val shadeViewControllerLazy: Lazy<ShadeViewController>,
+ private val commandQueue: CommandQueue,
private val shadeAnimationInteractor: ShadeAnimationInteractor,
private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
@@ -853,10 +853,11 @@ constructor(
if (dismissShade) {
return StatusBarTransitionAnimatorController(
animationController,
- shadeViewControllerLazy.get(),
shadeAnimationInteractor,
shadeControllerLazy.get(),
notifShadeWindowControllerLazy.get(),
+ commandQueue,
+ displayId,
isLaunchForActivity
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 3669ba851005..48d3157b1968 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -309,14 +309,13 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
if (mVibrateOnOpening) {
vibrateOnNavigationKeyDown();
}
- mShadeViewController.expand(true /* animate */);
+ mShadeController.animateExpandShade();
mNotificationStackScrollLayoutController.setWillExpand(true);
mHeadsUpManager.unpinAll(true /* userUnpinned */);
mMetricsLogger.count("panel_open", 1);
} else if (!mQsController.getExpanded()
&& !mShadeViewController.isExpandingOrCollapsing()) {
- mQsController.flingQs(0 /* velocity */,
- ShadeViewController.FLING_EXPAND);
+ mShadeController.animateExpandQs();
mMetricsLogger.count("panel_open_qs", 1);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index d10ca3d31de2..6b47ac113928 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -239,10 +239,22 @@ public class PhoneStatusBarView extends FrameLayout {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
layoutParams.height = mStatusBarHeight - waterfallTopInset;
+ updateSystemIconsContainerHeight();
updatePaddings();
setLayoutParams(layoutParams);
}
+ private void updateSystemIconsContainerHeight() {
+ View systemIconsContainer = findViewById(R.id.system_icons);
+ ViewGroup.LayoutParams layoutParams = systemIconsContainer.getLayoutParams();
+ int newSystemIconsHeight =
+ getResources().getDimensionPixelSize(R.dimen.status_bar_system_icons_height);
+ if (layoutParams.height != newSystemIconsHeight) {
+ layoutParams.height = newSystemIconsHeight;
+ systemIconsContainer.setLayoutParams(layoutParams);
+ }
+ }
+
private void updatePaddings() {
int statusBarPaddingStart = getResources().getDimensionPixelSize(
R.dimen.status_bar_padding_start);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index a39bfe00be28..f29ec8f38c27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone
import android.app.StatusBarManager.WINDOW_STATUS_BAR
import android.graphics.Point
import android.util.Log
+import android.view.InputDevice
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -81,7 +82,22 @@ private constructor(
statusContainer.setOnHoverListener(
statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)
)
- statusContainer.setOnClickListener { shadeViewController.expand(/* animate= */true) }
+ statusContainer.setOnTouchListener(object : View.OnTouchListener {
+ override fun onTouch(v: View, event: MotionEvent): Boolean {
+ // We want to handle only mouse events here to avoid stealing finger touches from
+ // status bar which expands shade when swiped down on. We're using onTouchListener
+ // instead of onClickListener as the later will lead to isClickable being set to
+ // true and hence ALL touches always being intercepted. See [View.OnTouchEvent]
+ if (event.source == InputDevice.SOURCE_MOUSE) {
+ if (event.action == MotionEvent.ACTION_UP) {
+ v.performClick()
+ shadeController.animateExpandShade()
+ }
+ return true
+ }
+ return false
+ }
+ })
progressProvider?.setReadyToHandleTransition(true)
configurationController.addCallback(configurationListener)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 29fd2258b40c..ba89d4ac22cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -23,7 +23,6 @@ import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConst
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.content.Context;
@@ -82,6 +81,9 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionListener;
@@ -93,11 +95,13 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.domain.interactor.StatusBarKeyguardViewManagerInteractor;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Lazy;
@@ -157,6 +161,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final BouncerView mPrimaryBouncerView;
private final Lazy<ShadeController> mShadeController;
+ private final Lazy<SceneInteractor> mSceneInteractorLazy;
// Local cache of expansion events, to avoid duplicates
private float mFraction = -1f;
@@ -347,6 +352,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor;
private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor;
private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor;
+ private final JavaAdapter mJavaAdapter;
+ private StatusBarKeyguardViewManagerInteractor mStatusBarKeyguardViewManagerInteractor;
@Inject
public StatusBarKeyguardViewManager(
@@ -378,7 +385,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
SelectedUserInteractor selectedUserInteractor,
- Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor
+ Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor,
+ JavaAdapter javaAdapter,
+ Lazy<SceneInteractor> sceneInteractorLazy,
+ StatusBarKeyguardViewManagerInteractor statusBarKeyguardViewManagerInteractor
) {
mContext = context;
mViewMediatorCallback = callback;
@@ -411,6 +421,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
mSelectedUserInteractor = selectedUserInteractor;
mSurfaceBehindInteractor = surfaceBehindInteractor;
+ mJavaAdapter = javaAdapter;
+ mSceneInteractorLazy = sceneInteractorLazy;
+ mStatusBarKeyguardViewManagerInteractor = statusBarKeyguardViewManagerInteractor;
}
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -481,8 +494,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
- collectFlow(
- getViewRootImpl().getView(),
+ mJavaAdapter.alwaysCollectFlow(
combineFlows(
mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
mSurfaceBehindInteractor.get().isAnimatingSurface(),
@@ -494,6 +506,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
lockscreenVis || animatingSurface
),
this::consumeShowStatusBarKeyguardView);
+
+ mJavaAdapter.alwaysCollectFlow(
+ mStatusBarKeyguardViewManagerInteractor.getKeyguardViewOcclusionState(),
+ (occlusionState) -> setOccluded(
+ occlusionState.getOccluded(), occlusionState.getAnimate()));
}
}
@@ -630,8 +647,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void show(Bundle options) {
Trace.beginSection("StatusBarKeyguardViewManager#show");
mNotificationShadeWindowController.setKeyguardShowing(true);
- mKeyguardStateController.notifyKeyguardState(true,
- mKeyguardStateController.isOccluded());
+ if (SceneContainerFlag.isEnabled()) {
+ mSceneInteractorLazy.get().changeScene(
+ Scenes.Lockscreen, "StatusBarKeyguardViewManager.show");
+ }
+ mKeyguardStateController.notifyKeyguardState(true, mKeyguardStateController.isOccluded());
reset(true /* hideBouncerWhenShowing */);
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
@@ -781,7 +801,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
- setKeyguardMessage(message, null);
+ setKeyguardMessage(message, null, null);
return;
}
@@ -1441,14 +1461,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
hideAlternateBouncer(false);
executeAfterKeyguardGoneAction();
}
+
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ mKeyguardTransitionInteractor.startDismissKeyguardTransition();
+ }
}
/** Display security message to relevant KeyguardMessageArea. */
- public void setKeyguardMessage(String message, ColorStateList colorState) {
+ public void setKeyguardMessage(String message, ColorStateList colorState,
+ BiometricSourceType biometricSourceType) {
if (mAlternateBouncerInteractor.isVisibleState()) {
if (mKeyguardMessageAreaController != null) {
DeviceEntryUdfpsRefactor.assertInLegacyMode();
- mKeyguardMessageAreaController.setMessage(message);
+ mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
}
} else {
mPrimaryBouncerInteractor.showMessage(message, colorState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 5610ed926f70..b5ab4e3eb462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -63,6 +63,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -105,6 +106,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final NotificationVisibilityProvider mVisibilityProvider;
private final HeadsUpManager mHeadsUpManager;
private final ActivityStarter mActivityStarter;
+ private final CommandQueue mCommandQueue;
private final NotificationClickNotifier mClickNotifier;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
@@ -143,6 +145,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
NotificationVisibilityProvider visibilityProvider,
HeadsUpManager headsUpManager,
ActivityStarter activityStarter,
+ CommandQueue commandQueue,
NotificationClickNotifier clickNotifier,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
@@ -175,6 +178,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mActivityStarter = activityStarter;
+ mCommandQueue = commandQueue;
mClickNotifier = clickNotifier;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
@@ -444,10 +448,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
ActivityTransitionAnimator.Controller animationController =
new StatusBarTransitionAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row, null),
- mShadeViewController,
mShadeAnimationInteractor,
mShadeController,
mNotificationShadeWindowController,
+ mCommandQueue,
+ mDisplayId,
isActivityIntent);
mActivityTransitionAnimator.startPendingIntentWithAnimation(
animationController,
@@ -486,10 +491,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
ActivityTransitionAnimator.Controller animationController =
new StatusBarTransitionAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row),
- mShadeViewController,
mShadeAnimationInteractor,
mShadeController,
mNotificationShadeWindowController,
+ mCommandQueue,
+ mDisplayId,
true /* isActivityIntent */);
mActivityTransitionAnimator.startIntentWithAnimation(
@@ -537,10 +543,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
viewController == null ? null
: new StatusBarTransitionAnimatorController(
viewController,
- mShadeViewController,
mShadeAnimationInteractor,
mShadeController,
mNotificationShadeWindowController,
+ mCommandQueue,
+ mDisplayId,
true /* isActivityIntent */);
mActivityTransitionAnimator.startIntentWithAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
index 7e907d80d277..705a11df83fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
@@ -3,10 +3,13 @@ package com.android.systemui.statusbar.phone
import android.view.View
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
+import com.android.systemui.animation.TransitionAnimator.Companion.getProgress
+import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
/**
* A [ActivityTransitionAnimator.Controller] that takes care of collapsing the status bar at the
@@ -14,12 +17,15 @@ import com.android.systemui.statusbar.NotificationShadeWindowController
*/
class StatusBarTransitionAnimatorController(
private val delegate: ActivityTransitionAnimator.Controller,
- private val shadeViewController: ShadeViewController,
private val shadeAnimationInteractor: ShadeAnimationInteractor,
private val shadeController: ShadeController,
private val notificationShadeWindowController: NotificationShadeWindowController,
+ private val commandQueue: CommandQueue,
+ @DisplayId private val displayId: Int,
private val isLaunchForActivity: Boolean = true
) : ActivityTransitionAnimator.Controller by delegate {
+ private var hideIconsDuringLaunchAnimation: Boolean = true
+
// Always sync the opening window with the shade, given that we draw a hole punch in the shade
// of the same size and position as the opening app to make it visible.
override val openingWindowSyncView: View?
@@ -38,7 +44,7 @@ class StatusBarTransitionAnimatorController(
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
shadeAnimationInteractor.setIsLaunchingActivity(true)
if (!isExpandingFullyAbove) {
- shadeViewController.collapseWithDuration(
+ shadeController.collapseWithDuration(
ActivityTransitionAnimator.TIMINGS.totalDuration.toInt()
)
}
@@ -56,7 +62,19 @@ class StatusBarTransitionAnimatorController(
linearProgress: Float
) {
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
- shadeViewController.applyLaunchAnimationProgress(linearProgress)
+ val hideIcons =
+ getProgress(
+ ActivityTransitionAnimator.TIMINGS,
+ linearProgress,
+ ANIMATION_DELAY_ICON_FADE_IN,
+ 100
+ ) == 0.0f
+ if (hideIcons != hideIconsDuringLaunchAnimation) {
+ hideIconsDuringLaunchAnimation = hideIcons
+ if (!hideIcons) {
+ commandQueue.recomputeDisableFlags(displayId, true /* animate */)
+ }
+ }
}
override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
@@ -64,4 +82,12 @@ class StatusBarTransitionAnimatorController(
shadeAnimationInteractor.setIsLaunchingActivity(false)
shadeController.onLaunchAnimationCancelled(isLaunchForActivity)
}
+
+ companion object {
+ val ANIMATION_DELAY_ICON_FADE_IN =
+ (ActivityTransitionAnimator.TIMINGS.totalDuration -
+ CollapsedStatusBarFragment.FADE_IN_DURATION -
+ CollapsedStatusBarFragment.FADE_IN_DELAY -
+ 48)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index f12a09b1062c..82d9fc7d0152 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -93,7 +93,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
/**
* @deprecated Don't subclass SystemUIDialog. Please subclass {@link Delegate} and pass it to
- * {@link Factory#create(DialogDelegate)} to create a custom dialog.
+ * {@link Factory#create(Delegate)} to create a custom dialog.
*/
@Deprecated
public SystemUIDialog(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index be843ba8699f..deae576662e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -22,7 +22,6 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
@@ -31,6 +30,8 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
@@ -58,9 +59,8 @@ constructor(
private val flags: FeatureFlagsClassic,
@Application private val scope: CoroutineScope,
) {
- @VisibleForTesting val mobileIconSubIdCache = mutableMapOf<Int, MobileIconViewModel>()
@VisibleForTesting
- val mobileIconInteractorSubIdCache = mutableMapOf<Int, MobileIconInteractor>()
+ val reuseCache = mutableMapOf<Int, Pair<MobileIconViewModel, CoroutineScope>>()
val subscriptionIdsFlow: StateFlow<List<Int>> =
interactor.filteredSubscriptions
@@ -109,24 +109,37 @@ constructor(
}
private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon {
- return mobileIconSubIdCache[subId]
- ?: MobileIconViewModel(
- subId,
- interactor.getMobileConnectionInteractorForSubId(subId),
- airplaneModeInteractor,
- constants,
- flags,
- scope,
- )
- .also { mobileIconSubIdCache[subId] = it }
+ return reuseCache.getOrPut(subId) { createViewModel(subId) }.first
}
- private fun invalidateCaches(subIds: List<Int>) {
- val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
- subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
+ private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> {
+ // Create a child scope so we can cancel it
+ val vmScope = scope.createChildScope()
+ val vm =
+ MobileIconViewModel(
+ subId,
+ interactor.getMobileConnectionInteractorForSubId(subId),
+ airplaneModeInteractor,
+ constants,
+ flags,
+ vmScope,
+ )
+
+ return Pair(vm, vmScope)
+ }
- mobileIconInteractorSubIdCache.keys
+ private fun CoroutineScope.createChildScope() =
+ CoroutineScope(coroutineContext + Job(coroutineContext[Job]))
+
+ private fun invalidateCaches(subIds: List<Int>) {
+ reuseCache.keys
.filter { !subIds.contains(it) }
- .forEach { subId -> mobileIconInteractorSubIdCache.remove(subId) }
+ .forEach { id ->
+ reuseCache
+ .remove(id)
+ // Cancel the view model's scope after removing it
+ ?.second
+ ?.cancel()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 2c1780d3b304..530e49c83802 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -304,14 +304,15 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
if (!isPinned) {
headsUpEntry.mWasUnpinned = true;
}
- if (entry.isRowPinned() != isPinned) {
- entry.setRowPinned(isPinned);
+ if (headsUpEntry.isPinned() != isPinned) {
+ headsUpEntry.setPinned(isPinned);
updatePinnedMode();
if (isPinned && entry.getSbn() != null) {
- mUiEventLogger.logWithInstanceId(
+ mUiEventLogger.logWithInstanceId(
NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
}
+ // TODO(b/325936094) convert these listeners to collecting a flow
for (OnHeadsUpChangedListener listener : mListeners) {
if (isPinned) {
listener.onHeadsUpPinned(entry);
@@ -662,6 +663,14 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
updateEntry(true /* updatePostTime */, "setEntry");
}
+ public boolean isPinned() {
+ return mEntry != null && mEntry.isRowPinned();
+ }
+
+ public void setPinned(boolean pinned) {
+ if (mEntry != null) mEntry.setRowPinned(pinned);
+ }
+
/**
* Updates an entry's removal time.
* @param updatePostTime whether or not to refresh the post time
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
index d19a3364d502..5c53ff98b777 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
@@ -16,8 +16,6 @@
package com.android.systemui.unfold
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.annotation.BinderThread
import android.content.Context
@@ -25,6 +23,7 @@ import android.os.Handler
import android.os.SystemProperties
import android.util.Log
import android.view.animation.DecelerateInterpolator
+import androidx.core.animation.addListener
import com.android.internal.foldables.FoldLockSettingAvailabilityProvider
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -37,25 +36,17 @@ import com.android.systemui.unfold.FullscreenLightRevealAnimationController.Comp
import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
import javax.inject.Inject
-import kotlin.coroutines.resume
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.android.asCoroutineDispatcher
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeout
-@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
class FoldLightRevealOverlayAnimation
@Inject
constructor(
@@ -70,9 +61,6 @@ constructor(
private val revealProgressValueAnimator: ValueAnimator =
ValueAnimator.ofFloat(ALPHA_OPAQUE, ALPHA_TRANSPARENT)
- private val areAnimationEnabled: Flow<Boolean>
- get() = animationStatusRepository.areAnimationsEnabled()
-
private lateinit var controller: FullscreenLightRevealAnimationController
@Volatile private var readyCallback: CompletableDeferred<Runnable>? = null
@@ -101,30 +89,33 @@ constructor(
applicationScope.launch(bgHandler.asCoroutineDispatcher()) {
deviceStateRepository.state
- .map { it == DeviceStateRepository.DeviceState.FOLDED }
+ .map { it != DeviceStateRepository.DeviceState.FOLDED }
.distinctUntilChanged()
- .flatMapLatest { isFolded ->
- flow<Nothing> {
- if (!areAnimationEnabled.first() || !isFolded) {
- return@flow
- }
- withTimeout(WAIT_FOR_ANIMATION_TIMEOUT_MS) {
- readyCallback = CompletableDeferred()
- val onReady = readyCallback?.await()
- readyCallback = null
- controller.addOverlay(ALPHA_OPAQUE, onReady)
- waitForScreenTurnedOn()
- }
- playFoldLightRevealOverlayAnimation()
- }
- .catchTimeoutAndLog()
- .onCompletion {
- val onReady = readyCallback?.takeIf { it.isCompleted }?.getCompleted()
- onReady?.run()
+ .filter { isUnfolded -> isUnfolded }
+ .collect { controller.ensureOverlayRemoved() }
+ }
+
+ applicationScope.launch(bgHandler.asCoroutineDispatcher()) {
+ deviceStateRepository.state
+ .filter {
+ animationStatusRepository.areAnimationsEnabled().first() &&
+ it == DeviceStateRepository.DeviceState.FOLDED
+ }
+ .collect {
+ try {
+ withTimeout(WAIT_FOR_ANIMATION_TIMEOUT_MS) {
+ readyCallback = CompletableDeferred()
+ val onReady = readyCallback?.await()
readyCallback = null
+ controller.addOverlay(ALPHA_OPAQUE, onReady)
+ waitForScreenTurnedOn()
+ playFoldLightRevealOverlayAnimation()
}
+ } catch (e: TimeoutCancellationException) {
+ Log.e(TAG, "Fold light reveal animation timed out")
+ ensureOverlayRemovedInternal()
+ }
}
- .collect {}
}
}
@@ -137,34 +128,19 @@ constructor(
powerInteractor.screenPowerState.filter { it == ScreenPowerState.SCREEN_ON }.first()
}
- private suspend fun playFoldLightRevealOverlayAnimation() {
+ private fun ensureOverlayRemovedInternal() {
+ revealProgressValueAnimator.cancel()
+ controller.ensureOverlayRemoved()
+ }
+
+ private fun playFoldLightRevealOverlayAnimation() {
revealProgressValueAnimator.duration = ANIMATION_DURATION
revealProgressValueAnimator.interpolator = DecelerateInterpolator()
revealProgressValueAnimator.addUpdateListener { animation ->
controller.updateRevealAmount(animation.animatedFraction)
}
- revealProgressValueAnimator.startAndAwaitCompletion()
- }
-
- private suspend fun ValueAnimator.startAndAwaitCompletion(): Unit =
- suspendCancellableCoroutine { continuation ->
- val listener =
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- continuation.resume(Unit)
- removeListener(this)
- }
- }
- addListener(listener)
- continuation.invokeOnCancellation { removeListener(listener) }
- start()
- }
-
- private fun <T> Flow<T>.catchTimeoutAndLog() = catch { exception ->
- when (exception) {
- is TimeoutCancellationException -> Log.e(TAG, "Fold light reveal animation timed out")
- else -> throw exception
- }
+ revealProgressValueAnimator.addListener(onEnd = { controller.ensureOverlayRemoved() })
+ revealProgressValueAnimator.start()
}
private companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt
new file mode 100644
index 000000000000..0128eb762296
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.policy.BatteryController
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.onStart
+
+fun BatteryController.isBatteryPowerSaveEnabled(): Flow<Boolean> {
+ return conflatedCallbackFlow {
+ val batteryCallback =
+ object : BatteryController.BatteryStateChangeCallback {
+ override fun onPowerSaveChanged(isPowerSave: Boolean) {
+ trySend(isPowerSave)
+ }
+ }
+ addCallback(batteryCallback)
+ awaitClose { removeCallback(batteryCallback) }
+ }
+ .onStart { emit(isPowerSave) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt
new file mode 100644
index 000000000000..22cc8dd7745d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.policy.RotationLockController
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.onStart
+
+fun RotationLockController.isRotationLockEnabled(): Flow<Boolean> {
+ return conflatedCallbackFlow {
+ val rotationLockCallback =
+ RotationLockController.RotationLockControllerCallback { rotationLocked, _ ->
+ trySend(rotationLocked)
+ }
+ addCallback(rotationLockCallback)
+ awaitClose { removeCallback(rotationLockCallback) }
+ }
+ .onStart { emit(isRotationLocked) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
index 0d6c0f59b2d0..10cf08221fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
@@ -25,8 +25,11 @@ import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
+import com.android.app.tracing.TraceUtils;
import com.android.systemui.settings.UserTracker;
+import kotlin.Unit;
+
/**
* Used to interact with per-user Settings.Secure and Settings.System settings (but not
* Settings.Global, since those do not vary per-user)
@@ -123,8 +126,16 @@ public interface UserSettingsProxy extends SettingsProxy {
default void registerContentObserverForUser(
Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
int userHandle) {
- getContentResolver().registerContentObserver(
- uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle));
+ TraceUtils.trace(
+ () -> {
+ // The limit for trace tags length is 127 chars, which leaves us 90 for Uri.
+ return "USP#registerObserver#[" + uri.toString() + "]";
+ }, () -> {
+ getContentResolver().registerContentObserver(
+ uri, notifyForDescendants, settingsObserver,
+ getRealUserHandle(userHandle));
+ return Unit.INSTANCE;
+ });
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 404563087041..23e296d70913 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -2082,6 +2082,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
} else {
row.anim.cancel();
row.anim.setIntValues(progress, newProgress);
+ // The animator can't keep up with the volume changes so haptics need to be
+ // triggered here. This happens when the volume keys are continuously pressed.
+ row.deliverOnProgressChangedHaptics(false, newProgress);
}
row.animTargetProgress = newProgress;
row.anim.setDuration(UPDATE_ANIMATION_DURATION);
@@ -2486,10 +2489,10 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
if (getActiveRow().equals(mRow)
&& mRow.slider.getVisibility() == VISIBLE
&& mRow.mHapticPlugin != null) {
- mRow.mHapticPlugin.onProgressChanged(seekBar, progress, fromUser);
- if (!fromUser) {
- // Consider a change from program as the volume key being continuously pressed
- mRow.mHapticPlugin.onKeyDown();
+ if (fromUser || mRow.animTargetProgress == progress) {
+ // Deliver user-generated slider changes immediately, or when the animation
+ // completes
+ mRow.deliverOnProgressChangedHaptics(fromUser, progress);
}
}
if (D.BUG) Log.d(TAG, AudioSystem.streamToString(mRow.stream)
@@ -2571,11 +2574,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
/* progressInterpolatorFactor= */ 1f,
/* progressBasedDragMinScale= */ 0f,
/* progressBasedDragMaxScale= */ 0.2f,
- /* additionalVelocityMaxBump= */ 0.15f,
+ /* additionalVelocityMaxBump= */ 0.25f,
/* deltaMillisForDragInterval= */ 0f,
- /* deltaProgressForDragThreshold= */ 0.015f,
- /* numberOfLowTicks= */ 5,
- /* maxVelocityToScale= */ 300f,
+ /* deltaProgressForDragThreshold= */ 0.05f,
+ /* numberOfLowTicks= */ 4,
+ /* maxVelocityToScale= */ 200,
/* velocityAxis= */ MotionEvent.AXIS_Y,
/* upperBookendScale= */ 1f,
/* lowerBookendScale= */ 0.05f,
@@ -2642,6 +2645,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
void removeHaptics() {
slider.setOnTouchListener(null);
}
+
+ void deliverOnProgressChangedHaptics(boolean fromUser, int progress) {
+ mHapticPlugin.onProgressChanged(slider, progress, fromUser);
+ if (!fromUser) {
+ // Consider a change from program as the volume key being continuously pressed
+ mHapticPlugin.onKeyDown();
+ }
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
index 170b32c1d0ea..2ab59984d06f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
@@ -16,14 +16,11 @@
package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
-import android.content.Intent
-import android.provider.Settings
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.media.dialog.MediaOutputDialogFactory
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
@@ -34,17 +31,8 @@ class MediaOutputActionsInteractor
@Inject
constructor(
private val mediaOutputDialogFactory: MediaOutputDialogFactory,
- private val activityStarter: ActivityStarter,
) {
- fun onDeviceClick(expandable: Expandable) {
- activityStarter.startActivity(
- Intent(Settings.ACTION_BLUETOOTH_SETTINGS),
- true,
- expandable.activityTransitionController(),
- )
- }
-
fun onBarClick(session: MediaDeviceSession, expandable: Expandable) {
when (session) {
is MediaDeviceSession.Active -> {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
index e518ed022792..37bf661454c5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
@@ -26,13 +26,13 @@ sealed interface DeviceIconViewModel {
val iconColor: Color
val backgroundColor: Color
- class IsPlaying(
+ data class IsPlaying(
override val icon: Icon,
override val iconColor: Color,
override val backgroundColor: Color,
) : DeviceIconViewModel
- class IsNotPlaying(
+ data class IsNotPlaying(
override val icon: Icon,
override val iconColor: Color,
override val backgroundColor: Color,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index 85d6c9e341ac..37661b53c98a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -113,11 +113,6 @@ constructor(
private fun MediaDeviceSession.isPlaying(): Boolean =
this is MediaDeviceSession.Active && playbackState?.isActive == true
- fun onDeviceClick(expandable: Expandable) {
- actionsInteractor.onDeviceClick(expandable)
- volumePanelViewModel.dismissPanel()
- }
-
fun onBarClick(expandable: Expandable) {
actionsInteractor.onBarClick(mediaDeviceSession.value, expandable)
volumePanelViewModel.dismissPanel()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt
index d90a9c75deec..485f4b5cbfd7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt
@@ -17,23 +17,18 @@
package com.android.systemui.volume.panel.shared.flag
import com.android.systemui.Flags
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.flags.RefactorFlagUtils
import javax.inject.Inject
/** Provides a flag to check for the new Compose based Volume Panel availability. */
class VolumePanelFlag @Inject constructor() {
- /**
- * Returns true when the new Volume Panel is available and false the otherwise. The new panel
- * can only be available when [ComposeFacade.isComposeAvailable] is true.
- */
+ /** Returns true when the new Volume Panel is available and false the otherwise. */
fun canUseNewVolumePanel(): Boolean {
- return ComposeFacade.isComposeAvailable() && Flags.newVolumePanel()
+ return Flags.newVolumePanel()
}
fun assertNewVolumePanel() {
- require(ComposeFacade.isComposeAvailable())
RefactorFlagUtils.assertInNewMode(Flags.newVolumePanel(), Flags.FLAG_NEW_VOLUME_PANEL)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
index 53e1b8b5bb70..d430e65770fd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
@@ -18,11 +18,12 @@ package com.android.systemui.volume.panel.ui.activity
import android.os.Bundle
import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag
+import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
import javax.inject.Provider
@@ -44,7 +45,7 @@ constructor(
volumePanelFlag.assertNewVolumePanel()
- ComposeFacade.setVolumePanelActivityContent(this, viewModel) { finish() }
+ setContent { VolumePanelRoot(viewModel = viewModel, onDismiss = ::finish) }
}
override fun onContentChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 65dede83f3d6..cb61534b2255 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -28,6 +28,7 @@ import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_N
import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.app.INotificationManager;
import android.app.Notification;
@@ -50,6 +51,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
@@ -97,6 +99,7 @@ public class BubblesManager {
private final Context mContext;
private final Bubbles mBubbles;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final KeyguardStateController mKeyguardStateController;
private final ShadeController mShadeController;
private final IStatusBarService mBarService;
private final INotificationManager mNotificationManager;
@@ -109,6 +112,7 @@ public class BubblesManager {
private final NotifPipeline mNotifPipeline;
private final NotifPipelineFlags mNotifPipelineFlags;
private final Executor mSysuiMainExecutor;
+ private final Executor mSysuiUiBgExecutor;
private final Bubbles.SysuiProxy mSysuiProxy;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
@@ -116,6 +120,8 @@ public class BubblesManager {
private final StatusBarWindowCallback mStatusBarWindowCallback;
private final Runnable mSensitiveStateChangedListener;
private boolean mPanelExpanded;
+ private boolean mKeyguardShowing;
+ private boolean mDreamingOrInPreview;
/**
* Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present
@@ -140,7 +146,8 @@ public class BubblesManager {
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- Executor sysuiMainExecutor) {
+ Executor sysuiMainExecutor,
+ Executor sysuiUiBgExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context,
bubblesOptional.get(),
@@ -160,7 +167,8 @@ public class BubblesManager {
sysUiState,
featureFlags,
notifPipelineFlags,
- sysuiMainExecutor);
+ sysuiMainExecutor,
+ sysuiUiBgExecutor);
} else {
return null;
}
@@ -185,10 +193,12 @@ public class BubblesManager {
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- Executor sysuiMainExecutor) {
+ Executor sysuiMainExecutor,
+ Executor sysuiUiBgExecutor) {
mContext = context;
mBubbles = bubbles;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mKeyguardStateController = keyguardStateController;
mShadeController = shadeController;
mNotificationManager = notificationManager;
mDreamManager = dreamManager;
@@ -200,6 +210,7 @@ public class BubblesManager {
mNotifPipeline = notifPipeline;
mNotifPipelineFlags = notifPipelineFlags;
mSysuiMainExecutor = sysuiMainExecutor;
+ mSysuiUiBgExecutor = sysuiUiBgExecutor;
mBarService = statusBarService == null
? IStatusBarService.Stub.asInterface(
@@ -208,12 +219,11 @@ public class BubblesManager {
setupNotifPipeline();
- keyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ // TODO(b/327410864): use KeyguardTransitionInteractor to listen for keyguard changes
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
- boolean isUnlockedShade = !keyguardStateController.isShowing()
- && !isDreamingOrInPreview();
- bubbles.onStatusBarStateChanged(isUnlockedShade);
+ updateKeyguardAndDreamingState();
}
});
@@ -256,6 +266,14 @@ public class BubblesManager {
mPanelExpanded = panelExpanded;
mBubbles.onNotificationPanelExpandedChanged(panelExpanded);
}
+ if (!mKeyguardShowing && mDreamingOrInPreview && !isDreaming) {
+ // We check for dreaming state changes when keyguard status changes.
+ // This causes us to miss events if dreaming state changes after keyguard.
+ // Add a check here for the case where keyguard is dismissed before
+ // dreaming state changes. Otherwise bubbles remain invisible.
+ // TODO(b/327410864): use KeyguardTransitionInteractor for dreaming changes
+ updateKeyguardAndDreamingState();
+ }
};
notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
@@ -395,6 +413,19 @@ public class BubblesManager {
mBubbles.setSysuiProxy(mSysuiProxy);
}
+ private void updateKeyguardAndDreamingState() {
+ mSysuiUiBgExecutor.execute(() -> {
+ mKeyguardShowing = mKeyguardStateController.isShowing();
+ mDreamingOrInPreview = isDreamingOrInPreview();
+ boolean isUnlockedShade = !mKeyguardShowing && !mDreamingOrInPreview;
+ ProtoLog.d(WM_SHELL_BUBBLES,
+ "handleKeyguardOrDreamChange isUnlockedShade=%b keyguardShowing=%b "
+ + "dreamingOrInPreview=%b",
+ isUnlockedShade, mKeyguardShowing, mDreamingOrInPreview);
+ mBubbles.onStatusBarStateChanged(isUnlockedShade);
+ });
+ }
+
private boolean isDreamingOrInPreview() {
try {
return mDreamManager.isDreamingOrInPreview();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 0f8a81399be8..3d0d8fb4abe4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -21,8 +21,8 @@ import android.view.View
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -319,7 +319,10 @@ class ClockEventControllerTest : SysuiTestCase() {
fun listenForDozeAmountTransition_updatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.dozeAmountTransition).thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.lockscreenToAodTransition)
+ .thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.aodToLockscreenTransition)
+ .thenReturn(transitionStep)
val job = underTest.listenForDozeAmountTransition(this)
transitionStep.value =
@@ -336,6 +339,48 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
+ fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
+ runBlocking(IMMEDIATE) {
+ val transitionStep = MutableStateFlow(TransitionStep())
+ whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ .thenReturn(transitionStep)
+
+ val job = underTest.listenForAnyStateToAodTransition(this)
+ transitionStep.value =
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ yield()
+
+ verify(animations, times(2)).doze(1f)
+
+ job.cancel()
+ }
+
+ @Test
+ fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
+ runBlocking(IMMEDIATE) {
+ val transitionStep = MutableStateFlow(TransitionStep())
+ whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ .thenReturn(transitionStep)
+
+ val job = underTest.listenForAnyStateToAodTransition(this)
+ transitionStep.value =
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ yield()
+
+ verify(animations, never()).doze(1f)
+
+ job.cancel()
+ }
+
+ @Test
fun unregisterListeners_validate() =
runBlocking(IMMEDIATE) {
underTest.unregisterListeners()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index d4522d003d52..93e7602715b1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -19,11 +19,14 @@ package com.android.keyguard;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricSourceType;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -119,4 +122,51 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
when(mKeyguardMessageArea.getText()).thenReturn(msg);
assertThat(mMessageAreaController.getMessage()).isEqualTo(msg);
}
+
+ @Test
+ public void testFingerprintMessageUpdate() {
+ String msg = "fpMessage";
+ mMessageAreaController.setMessage(
+ msg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(msg, /* animate= */ true);
+
+ String msg2 = "fpMessage2";
+ mMessageAreaController.setMessage(
+ msg2, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(msg2, /* animate= */ true);
+ }
+
+ @Test
+ public void testFaceMessageDroppedWhileFingerprintMessageShowing() {
+ String fpMsg = "fpMessage";
+ mMessageAreaController.setMessage(
+ fpMsg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+ String faceMessage = "faceMessage";
+ mMessageAreaController.setMessage(
+ faceMessage, BiometricSourceType.FACE
+ );
+ verify(mKeyguardMessageArea, never())
+ .setMessage(eq(faceMessage), /* animate= */ anyBoolean());
+ }
+
+ @Test
+ public void testGenericMessageShowsAfterFingerprintMessageShowing() {
+ String fpMsg = "fpMessage";
+ mMessageAreaController.setMessage(
+ fpMsg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+ String genericMessage = "genericMessage";
+ mMessageAreaController.setMessage(
+ genericMessage, null
+ );
+ verify(mKeyguardMessageArea)
+ .setMessage(eq(genericMessage), /* animate= */ anyBoolean());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 538daee52377..336a97ef09f4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -26,6 +26,8 @@ import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_DEFAULT;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -187,6 +189,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
TEST_CARRIER_ID, 0);
+ private static final SubscriptionInfo TEST_SUBSCRIPTION_PROVISIONING = new SubscriptionInfo(
+ 1, "", 0,
+ TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
+ DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
+ TEST_CARRIER_ID, PROFILE_CLASS_PROVISIONING);
private static final int FINGERPRINT_SENSOR_ID = 1;
@Mock
@@ -1204,7 +1211,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.mSimDatas.get(TEST_SUBSCRIPTION.getSubscriptionId()))
.isNotNull();
- when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(null);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList())
+ .thenReturn(new ArrayList<>());
mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mTestableLooper.processAllMessages();
@@ -1216,6 +1224,37 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testActiveSubscriptionList_filtersProvisioningNetworks() {
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION_PROVISIONING);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
+ mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+ assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).isEmpty();
+
+ SubscriptionInfo.Builder b = new SubscriptionInfo.Builder(TEST_SUBSCRIPTION_PROVISIONING);
+ b.setProfileClass(PROFILE_CLASS_DEFAULT);
+ SubscriptionInfo validInfo = b.build();
+
+ list.clear();
+ list.add(validInfo);
+ mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+ assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).hasSize(1);
+ }
+
+ @Test
+ public void testActiveSubscriptionList_filtersProvisioningNetworks_untilValid() {
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION_PROVISIONING);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
+ mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+ assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).isEmpty();
+
+ }
+
+ @Test
public void testIsUserUnlocked() {
// mUserManager will report the user as unlocked on @Before
assertThat(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 24a5e8072796..2e040077c227 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -15,14 +15,11 @@
package com.android.systemui;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
import android.os.Looper;
import androidx.test.filters.SmallTest;
-import com.android.systemui.statusbar.policy.FlashlightController;
-
import org.junit.Assert;
import org.junit.Test;
@@ -33,9 +30,9 @@ public class DependencyTest extends SysuiTestCase {
@Test
public void testClassDependency() {
- FlashlightController f = mock(FlashlightController.class);
- mDependency.injectTestDependency(FlashlightController.class, f);
- Assert.assertEquals(f, Dependency.get(FlashlightController.class));
+ FakeClass f = new FakeClass();
+ mDependency.injectTestDependency(FakeClass.class, f);
+ Assert.assertEquals(f, Dependency.get(FakeClass.class));
}
@Test
@@ -53,4 +50,8 @@ public class DependencyTest extends SysuiTestCase {
Dependency dependency = initializer.getSysUIComponent().createDependency();
dependency.start();
}
+
+ private static class FakeClass {
+
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 3da72618fb60..095c945ba77b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -22,10 +22,9 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGAT
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.display.DisplayManager;
@@ -75,13 +74,13 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
private AccessibilityManager mAccessibilityManager;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private AccessibilityFloatingMenuController mController;
+ @Mock
private AccessibilityButtonTargetsObserver mTargetsObserver;
+ @Mock
private AccessibilityButtonModeObserver mModeObserver;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
private KeyguardUpdateMonitorCallback mKeyguardCallback;
- private int mLastButtonMode;
- private String mLastButtonTargets;
@Mock
private SecureSettings mSecureSettings;
@@ -97,10 +96,14 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
mWindowManager = mContext.getSystemService(WindowManager.class);
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mLastButtonTargets = Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT);
- mLastButtonMode = Settings.Secure.getIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, UserHandle.USER_CURRENT);
+
+ when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
+ .thenReturn(Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT));
+
+ when(mModeObserver.getCurrentAccessibilityButtonMode())
+ .thenReturn(Settings.Secure.getIntForUser(mContextWrapper.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, UserHandle.USER_CURRENT));
}
@After
@@ -109,13 +112,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
mController.onAccessibilityButtonTargetsChanged("");
mController = null;
}
-
- Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, mLastButtonTargets,
- UserHandle.USER_CURRENT);
- Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mLastButtonMode,
- UserHandle.USER_CURRENT);
}
@Test
@@ -227,9 +223,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonModeChanged_floatingModeAndHasButtonTargets_showWidget() {
- Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- ActivityManager.getCurrentUser());
+ when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
+ .thenReturn(TEST_A11Y_BTN_TARGETS);
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
@@ -239,8 +234,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonModeChanged_floatingModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", ActivityManager.getCurrentUser());
+ when(mTargetsObserver.getCurrentAccessibilityButtonTargets()).thenReturn("");
+
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
@@ -250,9 +245,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonModeChanged_navBarModeAndHasButtonTargets_destroyWidget() {
- Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- ActivityManager.getCurrentUser());
+ when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
+ .thenReturn(TEST_A11Y_BTN_TARGETS);
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
@@ -262,8 +256,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonModeChanged_navBarModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", ActivityManager.getCurrentUser());
+ when(mTargetsObserver.getCurrentAccessibilityButtonTargets()).thenReturn("");
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
@@ -273,9 +266,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonTargetsChanged_floatingModeAndHasButtonTargets_showWidget() {
- Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
- ActivityManager.getCurrentUser());
+ when(mModeObserver.getCurrentAccessibilityButtonMode())
+ .thenReturn(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
@@ -285,9 +277,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonTargetsChanged_floatingModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
- ActivityManager.getCurrentUser());
+ when(mModeObserver.getCurrentAccessibilityButtonMode())
+ .thenReturn(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged("");
@@ -297,9 +288,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonTargetsChanged_navBarModeAndHasButtonTargets_destroyWidget() {
- Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, ActivityManager.getCurrentUser());
+ when(mModeObserver.getCurrentAccessibilityButtonMode())
+ .thenReturn(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
@@ -309,9 +299,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onAccessibilityButtonTargetsChanged_navBarModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, ActivityManager.getCurrentUser());
+ when(mModeObserver.getCurrentAccessibilityButtonMode())
+ .thenReturn(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged("");
@@ -321,9 +310,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Test
public void onTargetsChanged_isFloatingViewLayerControllerCreated() {
- Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
- UserHandle.USER_CURRENT);
+ when(mModeObserver.getCurrentAccessibilityButtonMode())
+ .thenReturn(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
@@ -335,8 +323,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
final FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
- mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class));
- mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
@@ -348,12 +334,11 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
}
private void enableAccessibilityFloatingMenuConfig() {
- Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
- ActivityManager.getCurrentUser());
- Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- ActivityManager.getCurrentUser());
+ when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
+ .thenReturn(TEST_A11Y_BTN_TARGETS);
+
+ when(mModeObserver.getCurrentAccessibilityButtonMode())
+ .thenReturn(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
}
private void captureKeyguardUpdateMonitorCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 2c1a87d86be9..10b86ea9fd31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -21,8 +21,8 @@ import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Handler
@@ -40,7 +40,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
@@ -148,8 +147,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Before
fun setup() {
- mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
displayRepository = FakeDisplayRepository()
displayStateInteractor =
@@ -388,9 +385,10 @@ open class AuthContainerViewTest : SysuiTestCase() {
}
@Test
- fun testShowCredentialUI() {
+ fun testShowCredentialUI_withDescription() {
+ mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
waitForIdleSync()
@@ -399,16 +397,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
}
@Test
- fun testShowBiometricUIWhenCustomBpEnabledAndNoSensors() {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ fun testShowCredentialUI_withCustomBp() {
val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ isUsingContentView = true
)
- waitForIdleSync()
-
- assertThat(customBiometricPrompt()).isTrue()
- assertThat(container.hasBiometricPrompt()).isTrue()
- assertThat(container.hasCredentialView()).isFalse()
+ checkBpShowsForCredentialAndGoToCredential(container)
}
@Test
@@ -512,11 +506,13 @@ open class AuthContainerViewTest : SysuiTestCase() {
private fun initializeFingerprintContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
- addToView: Boolean = true
+ addToView: Boolean = true,
+ isUsingContentView: Boolean = false,
) = initializeContainer(
TestAuthContainerView(
authenticators = authenticators,
- fingerprintProps = fingerprintSensorPropertiesInternal()
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
+ isUsingContentView = isUsingContentView,
),
addToView
)
@@ -549,7 +545,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
private inner class TestAuthContainerView(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
fingerprintProps: List<FingerprintSensorPropertiesInternal> = listOf(),
- faceProps: List<FaceSensorPropertiesInternal> = listOf()
+ faceProps: List<FaceSensorPropertiesInternal> = listOf(),
+ isUsingContentView: Boolean = false,
) : AuthContainerView(
Config().apply {
mContext = this@AuthContainerViewTest.context
@@ -559,6 +556,9 @@ open class AuthContainerViewTest : SysuiTestCase() {
mSkipAnimation = true
mPromptInfo = PromptInfo().apply {
this.authenticators = authenticators
+ if (isUsingContentView) {
+ this.contentView = PromptVerticalListContentView.Builder().build()
+ }
}
mOpPackageName = OP_PACKAGE_NAME
},
@@ -614,6 +614,17 @@ open class AuthContainerViewTest : SysuiTestCase() {
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.systemBars()) == 0).isTrue()
}
+
+ private fun checkBpShowsForCredentialAndGoToCredential(container: TestAuthContainerView) {
+ waitForIdleSync()
+ assertThat(container.hasBiometricPrompt()).isTrue()
+ assertThat(container.hasCredentialView()).isFalse()
+
+ container.animateToCredentialUI(false)
+ waitForIdleSync()
+ assertThat(container.hasBiometricPrompt()).isFalse()
+ assertThat(container.hasCredentialView()).isTrue()
+ }
}
private fun AuthContainerView.hasBiometricPrompt() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index b39e09df9d2e..7b972d3707af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -131,6 +133,52 @@ class PromptRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun showBpWithoutIconForCredential_withCustomBp() =
+ testScope.runTest {
+ for (case in
+ listOf(
+ PromptKind.Biometric(),
+ PromptKind.Pin,
+ PromptKind.Password,
+ PromptKind.Pattern
+ )) {
+ val hasCredentialViewShown = case !is PromptKind.Biometric
+ val promptInfo =
+ PromptInfo().apply {
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ contentView = PromptVerticalListContentView.Builder().build()
+ }
+ repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+ repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+ assertThat(repository.showBpWithoutIconForCredential.value)
+ .isEqualTo(!hasCredentialViewShown)
+ }
+ }
+
+ @Test
+ fun showBpWithoutIconForCredential_withDescription() =
+ testScope.runTest {
+ for (case in
+ listOf(
+ PromptKind.Biometric(),
+ PromptKind.Pin,
+ PromptKind.Password,
+ PromptKind.Pattern
+ )) {
+ val promptInfo =
+ PromptInfo().apply {
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ description = "description"
+ }
+ repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+ repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+ assertThat(repository.showBpWithoutIconForCredential.value).isFalse()
+ }
+ }
+
+ @Test
fun setsAndUnsetsPrompt() =
testScope.runTest {
val kind = PromptKind.Pin
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 52b42750847a..2817780cbf03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -147,7 +147,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() {
testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PIN) }
@Test
- fun usePattermCredentialAndReset() =
+ fun usePatternCredentialAndReset() =
testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PATTERN) }
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
index 3e6cc3bb4f6b..03e4f9ae1685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
@@ -32,10 +32,6 @@ import org.junit.runner.RunWith
class ComposeInitializerTest : SysuiTestCase() {
@Test
fun testCanAddComposeViewInInitializedWindow() {
- if (!ComposeFacade.isComposeAvailable()) {
- return
- }
-
val root = TestWindowRoot(context)
try {
runOnMainThreadAndWaitForIdleSync { ViewUtils.attachView(root) }
@@ -55,12 +51,12 @@ class ComposeInitializerTest : SysuiTestCase() {
class TestWindowRoot(context: Context) : FrameLayout(context) {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- ComposeFacade.composeInitializer().onAttachedToWindow(this)
+ ComposeInitializer.onAttachedToWindow(this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
- ComposeFacade.composeInitializer().onDetachedFromWindow(this)
+ ComposeInitializer.onDetachedFromWindow(this)
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index a992956f5121..59d8fc3d3fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -19,7 +19,6 @@ package com.android.systemui.keyboard.stickykeys.ui
import android.app.Dialog
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.keyboard.data.repository.FakeStickyKeysRepository
import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
@@ -34,7 +33,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
-import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,19 +52,22 @@ class StickyKeysIndicatorCoordinatorTest : SysuiTestCase() {
@Before
fun setup() {
- Assume.assumeTrue(ComposeFacade.isComposeAvailable())
val dialogFactory = mock<StickyKeyDialogFactory>()
whenever(dialogFactory.create(any())).thenReturn(dialog)
val keyboardRepository = Kosmos().keyboardRepository
- val viewModel = StickyKeysIndicatorViewModel(
+ val viewModel =
+ StickyKeysIndicatorViewModel(
stickyKeysRepository,
keyboardRepository,
- testScope.backgroundScope)
- coordinator = StickyKeysIndicatorCoordinator(
+ testScope.backgroundScope
+ )
+ coordinator =
+ StickyKeysIndicatorCoordinator(
testScope.backgroundScope,
dialogFactory,
viewModel,
- mock<StickyKeysLogger>())
+ mock<StickyKeysLogger>()
+ )
coordinator.startListening()
keyboardRepository.setIsAnyKeyboardConnected(true)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 915522de62d6..1a6da7608849 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -53,9 +53,11 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -101,6 +103,8 @@ class CustomizationProviderTest : SysuiTestCase() {
private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -185,6 +189,7 @@ class CustomizationProviderTest : SysuiTestCase() {
},
)
.keyguardInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 489665cd130a..51828c91de4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -6,6 +6,7 @@ import android.app.WindowConfiguration
import android.graphics.Point
import android.graphics.Rect
import android.os.PowerManager
+import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.RemoteAnimationTarget
@@ -15,6 +16,7 @@ import android.view.View
import android.view.ViewRootImpl
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
@@ -130,6 +132,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
* surface, or the user will see the wallpaper briefly as the app animates in.
*/
@Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun noSurfaceAnimation_ifWakeAndUnlocking() {
whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
@@ -320,6 +323,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
* If we are not wake and unlocking, we expect the unlock animation to play normally.
*/
@Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun surfaceAnimation_multipleTargets() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
arrayOf(remoteTarget1, remoteTarget2),
@@ -358,6 +362,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun surfaceBehindAlphaOverriddenTo0_ifNotInteractive() {
whenever(powerManager.isInteractive).thenReturn(false)
@@ -389,6 +394,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun surfaceBehindAlphaNotOverriddenTo0_ifInteractive() {
whenever(powerManager.isInteractive).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 0957748c9938..0bd4cbec64dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -1263,7 +1263,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mSystemPropertiesHelper,
() -> mock(WindowManagerLockscreenVisibilityManager.class),
mSelectedUserInteractor,
- mKeyguardInteractor);
+ mKeyguardInteractor,
+ mock(WindowManagerOcclusionManager.class));
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
new file mode 100644
index 000000000000..7bef01a7a5ce
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.PowerManager
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.testKosmos
+import junit.framework.Assert.assertEquals
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FromAodTransitionInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ }
+
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.fromAodTransitionInteractor
+
+ private val powerInteractor = kosmos.powerInteractor
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ @Before
+ fun setup() {
+ underTest.start()
+
+ // Transition to AOD and set the power interactor asleep.
+ powerInteractor.setAsleepForTest()
+ runBlocking {
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope
+ )
+ kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE)
+ reset(transitionRepository)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToLockscreen_onWakeup() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // Under default conditions, we should transition to LOCKSCREEN when waking up.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() =
+ testScope.runTest {
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.AOD,
+ to = KeyguardState.OCCLUDED,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() =
+ testScope.runTest {
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() =
+ testScope.runTest {
+ kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ runCurrent()
+
+ // Make sure we're GONE.
+ assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+
+ // Get part way to AOD.
+ powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
+ runCurrent()
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING,
+ )
+
+ // Detect a power gesture and then wake up.
+ reset(transitionRepository)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ runCurrent()
+
+ // Make sure we're GONE.
+ assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+
+ // Get all the way to AOD
+ powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+
+ // Detect a power gesture and then wake up.
+ reset(transitionRepository)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should go to OCCLUDED - we came from GONE, but we finished in AOD, so this is no
+ // longer an insecure camera launch and it would be bad if we unlocked now.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+ runCurrent()
+
+ // Make sure we're in LOCKSCREEN.
+ assertEquals(
+ KeyguardState.LOCKSCREEN,
+ kosmos.keyguardTransitionInteractor.getFinishedState()
+ )
+
+ // Get part way to AOD.
+ powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
+ runCurrent()
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING,
+ )
+
+ // Detect a power gesture and then wake up.
+ reset(transitionRepository)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testWakeAndUnlock_transitionsToGone_onlyAfterDismissCallPostWakeup() =
+ testScope.runTest {
+ kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // Waking up from wake and unlock should not start any transitions, we'll wait for the
+ // dismiss call.
+ assertThat(transitionRepository).noTransitionsStarted()
+
+ underTest.dismissAod()
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
new file mode 100644
index 000000000000..258dbf3efbae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.PowerManager
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.testKosmos
+import junit.framework.Assert.assertEquals
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FromDozingTransitionInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ }
+
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.fromDozingTransitionInteractor
+
+ private val powerInteractor = kosmos.powerInteractor
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ @Before
+ fun setup() {
+ underTest.start()
+
+ // Transition to DOZING and set the power interactor asleep.
+ powerInteractor.setAsleepForTest()
+ runBlocking {
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ testScope
+ )
+ kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE)
+ reset(transitionRepository)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToLockscreen_onWakeup() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // Under default conditions, we should transition to LOCKSCREEN when waking up.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() =
+ testScope.runTest {
+ kosmos.fakeCommunalRepository.setTransitionState(
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+ )
+ runCurrent()
+
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // Under default conditions, we should transition to LOCKSCREEN when waking up.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() =
+ testScope.runTest {
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.OCCLUDED,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop_evenIfIdleOnCommunal() =
+ testScope.runTest {
+ kosmos.fakeCommunalRepository.setTransitionState(
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+ )
+ runCurrent()
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.OCCLUDED,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() =
+ testScope.runTest {
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() =
+ testScope.runTest {
+ kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GONE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ runCurrent()
+
+ // Make sure we're GONE.
+ assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+
+ // Get part way to AOD.
+ powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
+ runCurrent()
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.DOZING,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING,
+ )
+
+ // Detect a power gesture and then wake up.
+ reset(transitionRepository)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GONE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ runCurrent()
+
+ // Make sure we're GONE.
+ assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+
+ // Get all the way to AOD
+ powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.DOZING,
+ testScope = testScope,
+ )
+
+ // Detect a power gesture and then wake up.
+ reset(transitionRepository)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should go to OCCLUDED - we came from GONE, but we finished in AOD, so this is no
+ // longer an insecure camera launch and it would be bad if we unlocked now.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+ runCurrent()
+
+ // Make sure we're in LOCKSCREEN.
+ assertEquals(
+ KeyguardState.LOCKSCREEN,
+ kosmos.keyguardTransitionInteractor.getFinishedState()
+ )
+
+ // Get part way to AOD.
+ powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
+ runCurrent()
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING,
+ )
+
+ // Detect a power gesture and then wake up.
+ reset(transitionRepository)
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // We should head back to GONE since we started there.
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
new file mode 100644
index 000000000000..f534ba5bc68c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.testKosmos
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FromDreamingTransitionInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ }
+
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.fromDreamingTransitionInteractor
+
+ private val powerInteractor = kosmos.powerInteractor
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ @Before
+ fun setup() {
+ underTest.start()
+
+ // Transition to DOZING and set the power interactor asleep.
+ powerInteractor.setAsleepForTest()
+ runBlocking {
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ testScope
+ )
+ kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE)
+ reset(transitionRepository)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_ifDreamEnds_occludingActivityOnTop() =
+ testScope.runTest {
+ kosmos.fakeKeyguardRepository.setDreaming(true)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ testScope,
+ )
+ runCurrent()
+
+ reset(transitionRepository)
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true)
+ kosmos.fakeKeyguardRepository.setDreaming(false)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.OCCLUDED,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testDoesNotTransitionToOccluded_occludingActivityOnTop_whileStillDreaming() =
+ testScope.runTest {
+ kosmos.fakeKeyguardRepository.setDreaming(true)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ testScope,
+ )
+ runCurrent()
+
+ reset(transitionRepository)
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true)
+ runCurrent()
+
+ assertThat(transitionRepository).noTransitionsStarted()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionsToLockscreen_whenOccludingActivityEnds() =
+ testScope.runTest {
+ kosmos.fakeKeyguardRepository.setDreaming(true)
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ testScope,
+ )
+ runCurrent()
+
+ reset(transitionRepository)
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 6d8e7aa0703c..c3e24d579491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.domain.interactor
+import android.app.ActivityManager
+import android.app.WindowConfiguration
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -26,6 +28,7 @@ import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -42,6 +45,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
@@ -126,7 +130,7 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
keyguardRepository.setKeyguardDismissible(true)
runCurrent()
shadeRepository.setCurrentFling(
- FlingInfo(expand = true) // Not a dismiss fling (expand = true).
+ FlingInfo(expand = false) // Is a dismiss fling upward (expand = false).
)
runCurrent()
@@ -153,4 +157,67 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
assertThatRepository(transitionRepository).noTransitionsStarted()
}
+
+ @Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testDoesNotTransitionToGone_whenDismissFling_emitsNull() =
+ testScope.runTest {
+ underTest.start()
+ verify(transitionRepository, never()).startTransition(any())
+
+ keyguardRepository.setKeyguardDismissible(true)
+ runCurrent()
+
+ // The fling is null when it a) initializes b) ends and in either case we should not
+ // swipe to unlock.
+ shadeRepository.setCurrentFling(null)
+ runCurrent()
+
+ assertThatRepository(transitionRepository).noTransitionsStarted()
+ }
+
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionsToOccluded_whenShowWhenLockedActivityOnTop() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ reset(transitionRepository)
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(
+ true,
+ ActivityManager.RunningTaskInfo().apply {
+ topActivityType = WindowConfiguration.ACTIVITY_TYPE_STANDARD
+ }
+ )
+ runCurrent()
+
+ assertThatRepository(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionsToDream_whenDreamActivityOnTop() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ reset(transitionRepository)
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(
+ true,
+ ActivityManager.RunningTaskInfo().apply {
+ topActivityType = WindowConfiguration.ACTIVITY_TYPE_DREAM
+ }
+ )
+ runCurrent()
+
+ assertThatRepository(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
new file mode 100644
index 000000000000..d3c48483d100
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.testKosmos
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FromOccludedTransitionInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ }
+
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.fromOccludedTransitionInteractor
+
+ private val powerInteractor = kosmos.powerInteractor
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ @Before
+ fun setup() {
+ underTest.start()
+
+ // Transition to OCCLUDED and set up PowerInteractor and the occlusion repository.
+ powerInteractor.setAwakeForTest()
+ runBlocking {
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope
+ )
+ reset(transitionRepository)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testShowWhenLockedActivity_noLongerOnTop_transitionsToLockscreen() =
+ testScope.runTest {
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testShowWhenLockedActivity_noLongerOnTop_transitionsToGlanceableHub_ifIdleOnCommunal() =
+ testScope.runTest {
+ kosmos.fakeCommunalRepository.setTransitionState(
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+ )
+ runCurrent()
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.GLANCEABLE_HUB,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index f33a5c9484ee..7ee8963aaa15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -16,14 +16,23 @@
package com.android.systemui.keyguard.domain.interactor
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.selectedUserInteractor
@@ -31,20 +40,27 @@ import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import junit.framework.Assert.fail
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class FromPrimaryBouncerTransitionInteractorTest : SysuiTestCase() {
- val kosmos = testKosmos()
+ val kosmos =
+ testKosmos().apply {
+ this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ }
val underTest = kosmos.fromPrimaryBouncerTransitionInteractor
val testScope = kosmos.testScope
val selectedUserInteractor = kosmos.selectedUserInteractor
val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
@Test
fun testSurfaceBehindVisibility() =
@@ -193,4 +209,85 @@ class FromPrimaryBouncerTransitionInteractorTest : SysuiTestCase() {
fail("surfaceBehindModel was unexpectedly null.")
}
}
+
+ @Test
+ @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testReturnToLockscreen_whenBouncerHides() =
+ testScope.runTest {
+ underTest.start()
+ bouncerRepository.setPrimaryShow(true)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ testScope
+ )
+
+ reset(transitionRepository)
+
+ bouncerRepository.setPrimaryShow(false)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN
+ )
+ }
+
+ @Test
+ @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testReturnToGlanceableHub_whenBouncerHides_ifIdleOnCommunal() =
+ testScope.runTest {
+ underTest.start()
+ kosmos.fakeCommunalRepository.setTransitionState(
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+ )
+ bouncerRepository.setPrimaryShow(true)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ testScope
+ )
+
+ reset(transitionRepository)
+
+ bouncerRepository.setPrimaryShow(false)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GLANCEABLE_HUB
+ )
+ }
+
+ @Test
+ @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToOccluded_bouncerHide_occludingActivityOnTop() =
+ testScope.runTest {
+ underTest.start()
+ bouncerRepository.setPrimaryShow(true)
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ testScope
+ )
+
+ reset(transitionRepository)
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ runCurrent()
+
+ // Shouldn't transition to OCCLUDED until the bouncer hides.
+ assertThat(transitionRepository).noTransitionsStarted()
+
+ bouncerRepository.setPrimaryShow(false)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.OCCLUDED
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
new file mode 100644
index 000000000000..8a77ed2130a9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import kotlin.test.Test
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardOcclusionInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.keyguardOcclusionInteractor
+ private val powerInteractor = kosmos.powerInteractor
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ @Test
+ fun testTransitionFromPowerGesture_whileGoingToSleep_isTrue() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING
+ )
+
+ powerInteractor.onCameraLaunchGestureDetected()
+ runCurrent()
+
+ assertTrue(underTest.shouldTransitionFromPowerButtonGesture())
+ }
+
+ @Test
+ fun testTransitionFromPowerGesture_whileAsleep_isTrue() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+
+ powerInteractor.onCameraLaunchGestureDetected()
+ runCurrent()
+
+ assertTrue(underTest.shouldTransitionFromPowerButtonGesture())
+ }
+
+ @Test
+ fun testTransitionFromPowerGesture_whileWaking_isFalse() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING
+ )
+
+ powerInteractor.onCameraLaunchGestureDetected()
+ runCurrent()
+
+ assertFalse(underTest.shouldTransitionFromPowerButtonGesture())
+ }
+
+ @Test
+ fun testTransitionFromPowerGesture_whileAwake_isFalse() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+
+ powerInteractor.onCameraLaunchGestureDetected()
+ runCurrent()
+
+ assertFalse(underTest.shouldTransitionFromPowerButtonGesture())
+ }
+
+ @Test
+ fun testShowWhenLockedActivityLaunchedFromPowerGesture_notTrueSecondTime() =
+ testScope.runTest {
+ val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture)
+ powerInteractor.setAsleepForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+
+ powerInteractor.onCameraLaunchGestureDetected()
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ false,
+ true,
+ )
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ false,
+ true,
+ false,
+ )
+
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ false,
+ true,
+ // Power button gesture was not triggered a second time, so this should remain
+ // false.
+ false,
+ )
+ }
+
+ @Test
+ fun testShowWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone() =
+ testScope.runTest {
+ val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture)
+ powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+
+ powerInteractor.setAsleepForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING
+ )
+
+ powerInteractor.onCameraLaunchGestureDetected()
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+
+ assertThat(values)
+ .containsExactly(
+ false,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index d2a8444e40ee..45b2a4266ff6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -46,7 +46,9 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -242,6 +244,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var userTracker: UserTracker
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -311,6 +315,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
featureFlags = featureFlags,
)
.keyguardInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 69cd173f4253..95606ae81e5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -18,15 +18,15 @@ package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
+import com.android.systemui.Flags
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeCommandQueue
@@ -40,7 +40,6 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -48,7 +47,6 @@ import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.statusbar.commandQueue
import com.android.systemui.testKosmos
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -92,30 +90,26 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
private var commandQueue = kosmos.fakeCommandQueue
private val shadeRepository = kosmos.fakeShadeRepository
private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
- private val transitionInteractor = kosmos.keyguardTransitionInteractor
private lateinit var featureFlags: FakeFeatureFlags
// Used to verify transition requests for test output
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
- @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private val fromLockscreenTransitionInteractor = kosmos.fromLockscreenTransitionInteractor
- private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
- private lateinit var fromDozingTransitionInteractor: FromDozingTransitionInteractor
- private lateinit var fromOccludedTransitionInteractor: FromOccludedTransitionInteractor
- private lateinit var fromGoneTransitionInteractor: FromGoneTransitionInteractor
- private lateinit var fromAodTransitionInteractor: FromAodTransitionInteractor
- private lateinit var fromAlternateBouncerTransitionInteractor:
- FromAlternateBouncerTransitionInteractor
+ private val fromDreamingTransitionInteractor = kosmos.fromDreamingTransitionInteractor
+ private val fromDozingTransitionInteractor = kosmos.fromDozingTransitionInteractor
+ private val fromOccludedTransitionInteractor = kosmos.fromOccludedTransitionInteractor
+ private val fromGoneTransitionInteractor = kosmos.fromGoneTransitionInteractor
+ private val fromAodTransitionInteractor = kosmos.fromAodTransitionInteractor
+ private val fromAlternateBouncerTransitionInteractor =
+ kosmos.fromAlternateBouncerTransitionInteractor
private val fromPrimaryBouncerTransitionInteractor =
kosmos.fromPrimaryBouncerTransitionInteractor
- private lateinit var fromDreamingLockscreenHostedTransitionInteractor:
- FromDreamingLockscreenHostedTransitionInteractor
- private lateinit var fromGlanceableHubTransitionInteractor:
- FromGlanceableHubTransitionInteractor
+ private val fromDreamingLockscreenHostedTransitionInteractor =
+ kosmos.fromDreamingLockscreenHostedTransitionInteractor
+ private val fromGlanceableHubTransitionInteractor = kosmos.fromGlanceableHubTransitionInteractor
private val powerInteractor = kosmos.powerInteractor
- private val keyguardInteractor = kosmos.keyguardInteractor
private val communalInteractor = kosmos.communalInteractor
@Before
@@ -125,121 +119,21 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+ )
featureFlags = FakeFeatureFlags()
- val glanceableHubTransitions =
- GlanceableHubTransitions(
- bgDispatcher = kosmos.testDispatcher,
- transitionInteractor = transitionInteractor,
- transitionRepository = transitionRepository,
- communalInteractor = communalInteractor
- )
-
fromLockscreenTransitionInteractor.start()
fromPrimaryBouncerTransitionInteractor.start()
-
- fromDreamingTransitionInteractor =
- FromDreamingTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- glanceableHubTransitions = glanceableHubTransitions,
- )
- .apply { start() }
-
- fromDreamingLockscreenHostedTransitionInteractor =
- FromDreamingLockscreenHostedTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- )
- .apply { start() }
-
- fromAodTransitionInteractor =
- FromAodTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- )
- .apply { start() }
-
- fromGoneTransitionInteractor =
- FromGoneTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- powerInteractor = powerInteractor,
- communalInteractor = communalInteractor,
- )
- .apply { start() }
-
- fromDozingTransitionInteractor =
- FromDozingTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- powerInteractor = powerInteractor,
- communalInteractor = communalInteractor,
- )
- .apply { start() }
-
- fromOccludedTransitionInteractor =
- FromOccludedTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- powerInteractor = powerInteractor,
- communalInteractor = communalInteractor,
- )
- .apply { start() }
-
- fromAlternateBouncerTransitionInteractor =
- FromAlternateBouncerTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- communalInteractor = communalInteractor,
- powerInteractor = powerInteractor,
- )
- .apply { start() }
-
- fromGlanceableHubTransitionInteractor =
- FromGlanceableHubTransitionInteractor(
- scope = testScope,
- bgDispatcher = kosmos.testDispatcher,
- mainDispatcher = kosmos.testDispatcher,
- glanceableHubTransitions = glanceableHubTransitions,
- keyguardInteractor = keyguardInteractor,
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- powerInteractor = powerInteractor,
- )
- .apply { start() }
-
- mSetFlagsRule.disableFlags(
- FLAG_KEYGUARD_WM_STATE_REFACTOR,
- )
+ fromDreamingTransitionInteractor.start()
+ fromDreamingLockscreenHostedTransitionInteractor.start()
+ fromAodTransitionInteractor.start()
+ fromGoneTransitionInteractor.start()
+ fromDozingTransitionInteractor.start()
+ fromOccludedTransitionInteractor.start()
+ fromAlternateBouncerTransitionInteractor.start()
+ fromGlanceableHubTransitionInteractor.start()
}
@Test
@@ -256,7 +150,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
.startedTransition(
to = KeyguardState.PRIMARY_BOUNCER,
from = KeyguardState.LOCKSCREEN,
- ownerName = "FromLockscreenTransitionInteractor",
+ ownerName =
+ "FromLockscreenTransitionInteractor" +
+ "(#listenForLockscreenToPrimaryBouncer)",
animatorAssertion = { it.isNotNull() }
)
@@ -281,7 +177,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
.startedTransition(
to = KeyguardState.DOZING,
from = KeyguardState.OCCLUDED,
- ownerName = "FromOccludedTransitionInteractor",
+ ownerName = "FromOccludedTransitionInteractor(Sleep transition triggered)",
animatorAssertion = { it.isNotNull() }
)
@@ -306,7 +202,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
.startedTransition(
to = KeyguardState.AOD,
from = KeyguardState.OCCLUDED,
- ownerName = "FromOccludedTransitionInteractor",
+ ownerName = "FromOccludedTransitionInteractor(Sleep transition triggered)",
animatorAssertion = { it.isNotNull() }
)
@@ -388,7 +284,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
.startedTransition(
to = KeyguardState.DOZING,
from = KeyguardState.LOCKSCREEN,
- ownerName = "FromLockscreenTransitionInteractor",
+ ownerName = "FromLockscreenTransitionInteractor(Sleep transition triggered)",
animatorAssertion = { it.isNotNull() }
)
@@ -413,7 +309,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
.startedTransition(
to = KeyguardState.AOD,
from = KeyguardState.LOCKSCREEN,
- ownerName = "FromLockscreenTransitionInteractor",
+ ownerName = "FromLockscreenTransitionInteractor(Sleep transition triggered)",
animatorAssertion = { it.isNotNull() }
)
@@ -574,8 +470,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runCurrent()
// WHEN the device begins to wake
+ keyguardRepository.setKeyguardShowing(true)
powerInteractor.setAwakeForTest()
- runCurrent()
+ advanceTimeBy(60L)
assertThat(transitionRepository)
.startedTransition(
@@ -630,15 +527,66 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
- fun dozingToGone() =
+ fun dozingToGoneWithUnlock() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
+ runCurrent()
// WHEN biometrics succeeds with wake and unlock mode
+ powerInteractor.setAwakeForTest()
keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ advanceTimeBy(60L)
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GONE,
+ from = KeyguardState.DOZING,
+ ownerName = "FromDozingTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun dozingToPrimaryBouncer() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DOZING
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
+ runCurrent()
+
+ // WHEN awaked by a request to show the primary bouncer, as can happen if SPFS is
+ // touched after boot
+ powerInteractor.setAwakeForTest()
+ bouncerRepository.setPrimaryShow(true)
+ advanceTimeBy(60L)
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.PRIMARY_BOUNCER,
+ from = KeyguardState.DOZING,
+ ownerName = "FromDozingTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ /** This handles security method NONE and screen off with lock timeout */
+ @Test
+ fun dozingToGoneWithKeyguardNotShowing() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DOZING
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
runCurrent()
+ // WHEN the device wakes up without a keyguard
+ keyguardRepository.setKeyguardShowing(false)
+ keyguardRepository.setKeyguardDismissible(true)
+ powerInteractor.setAwakeForTest()
+ advanceTimeBy(60L)
+
assertThat(transitionRepository)
.startedTransition(
to = KeyguardState.GONE,
@@ -650,6 +598,32 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
+ /** This handles security method NONE and screen off with lock timeout */
+ @Test
+ fun dreamingToGoneWithKeyguardNotShowing() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DREAMING
+ keyguardRepository.setDreamingWithOverlay(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+ runCurrent()
+
+ // WHEN the device wakes up without a keyguard
+ keyguardRepository.setKeyguardShowing(false)
+ keyguardRepository.setKeyguardDismissible(true)
+ keyguardRepository.setDreamingWithOverlay(false)
+ advanceTimeBy(60L)
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GONE,
+ from = KeyguardState.DREAMING,
+ ownerName = "FromDreamingTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
@Test
fun dozingToGlanceableHub() =
testScope.runTest {
@@ -659,15 +633,16 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the device is idle on the glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
// WHEN the device begins to wake
+ keyguardRepository.setKeyguardShowing(true)
powerInteractor.setAwakeForTest()
- runCurrent()
+ advanceTimeBy(60L)
assertThat(transitionRepository)
.startedTransition(
@@ -698,7 +673,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
.startedTransition(
to = KeyguardState.DOZING,
from = KeyguardState.GONE,
- ownerName = "FromGoneTransitionInteractor",
+ ownerName = "FromGoneTransitionInteractor(Sleep transition triggered)",
animatorAssertion = { it.isNotNull() }
)
@@ -723,7 +698,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
.startedTransition(
to = KeyguardState.AOD,
from = KeyguardState.GONE,
- ownerName = "FromGoneTransitionInteractor",
+ ownerName = "FromGoneTransitionInteractor(Sleep transition triggered)",
animatorAssertion = { it.isNotNull() }
)
@@ -816,8 +791,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the device is idle on the glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -965,8 +940,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the device is idle on the glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -990,12 +965,14 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
@Test
fun primaryBouncerToAod() =
testScope.runTest {
+ // GIVEN aod available
+ keyguardRepository.setAodAvailable(true)
+ runCurrent()
+
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
- // GIVEN aod available and starting to sleep
- keyguardRepository.setAodAvailable(true)
powerInteractor.setAsleepForTest()
// WHEN the primaryBouncer stops showing
@@ -1005,7 +982,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// THEN a transition to AOD should occur
assertThat(transitionRepository)
.startedTransition(
- ownerName = "FromPrimaryBouncerTransitionInteractor",
+ ownerName =
+ "FromPrimaryBouncerTransitionInteractor" + "(Sleep transition triggered)",
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.AOD,
animatorAssertion = { it.isNotNull() },
@@ -1032,7 +1010,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// THEN a transition to DOZING should occur
assertThat(transitionRepository)
.startedTransition(
- ownerName = "FromPrimaryBouncerTransitionInteractor",
+ ownerName =
+ "FromPrimaryBouncerTransitionInteractor" + "(Sleep transition triggered)",
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.DOZING,
animatorAssertion = { it.isNotNull() },
@@ -1073,8 +1052,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the device is idle on the glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -1108,8 +1087,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the device is idle on the glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -1226,8 +1205,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the device is idle on the glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -1335,8 +1314,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// WHEN the keyguard is occluded and device wakes up
keyguardRepository.setKeyguardOccluded(true)
+ keyguardRepository.setKeyguardShowing(true)
powerInteractor.setAwakeForTest()
- runCurrent()
+ advanceTimeBy(60L)
// THEN a transition to OCCLUDED should occur
assertThat(transitionRepository)
@@ -1414,13 +1394,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runCurrent()
// WHEN a transition to the glanceable hub starts
- val currentScene = CommunalSceneKey.Blank
- val targetScene = CommunalSceneKey.Communal
+ val currentScene = CommunalScenes.Blank
+ val targetScene = CommunalScenes.Communal
val progress = MutableStateFlow(0f)
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Transition(
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
fromScene = currentScene,
toScene = targetScene,
progress = progress,
@@ -1561,7 +1541,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
assertThat(transitionRepository)
.startedTransition(
- ownerName = "FromLockscreenTransitionInteractor",
+ ownerName =
+ "FromLockscreenTransitionInteractor" +
+ "(#listenForLockscreenToPrimaryBouncerDragging)",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.PRIMARY_BOUNCER,
animatorAssertion = { it.isNull() }, // dragging should be manually animated
@@ -1593,13 +1575,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runCurrent()
// WHEN a glanceable hub transition starts
- val currentScene = CommunalSceneKey.Blank
- val targetScene = CommunalSceneKey.Communal
+ val currentScene = CommunalScenes.Blank
+ val targetScene = CommunalScenes.Communal
val progress = MutableStateFlow(0f)
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Transition(
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
fromScene = currentScene,
toScene = targetScene,
progress = progress,
@@ -1624,8 +1606,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
clearInvocations(transitionRepository)
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(currentScene)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(currentScene)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -1649,13 +1631,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runCurrent()
// WHEN a transition away from glanceable hub starts
- val currentScene = CommunalSceneKey.Communal
- val targetScene = CommunalSceneKey.Blank
+ val currentScene = CommunalScenes.Communal
+ val targetScene = CommunalScenes.Blank
val progress = MutableStateFlow(0f)
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Transition(
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
fromScene = currentScene,
toScene = targetScene,
progress = progress,
@@ -1679,8 +1661,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
clearInvocations(transitionRepository)
runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.LOCKSCREEN)
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(currentScene)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(currentScene)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -1766,8 +1748,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// GIVEN the device is idle on the glanceable hub
val idleTransitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
)
communalInteractor.setTransitionState(idleTransitionState)
runCurrent()
@@ -1823,12 +1805,12 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runCurrent()
// WHEN a transition away from glanceable hub starts
- val currentScene = CommunalSceneKey.Communal
- val targetScene = CommunalSceneKey.Blank
+ val currentScene = CommunalScenes.Communal
+ val targetScene = CommunalScenes.Blank
val transitionState =
- MutableStateFlow<ObservableCommunalTransitionState>(
- ObservableCommunalTransitionState.Transition(
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
fromScene = currentScene,
toScene = targetScene,
progress = flowOf(0f, 0.1f),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index a4483bdac467..6d605a564022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -375,4 +375,323 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
values
)
}
+
+ @Test
+ fun testLockscreenVisibility_usesFromState_ifCanceled() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockscreenVisibility)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ // Initially should be true, as we start in LOCKSCREEN.
+ true,
+ // Then, false, since we finish in GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ // Should remain false as we transition from GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ )
+
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // If we cancel and then go from LS -> GONE, we should immediately flip to the
+ // visibility of the from state (LS).
+ true,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ )
+
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ true,
+ ),
+ values
+ )
+ }
+
+ /**
+ * Tests the special case for insecure camera launch. CANCELING a transition from GONE and then
+ * STARTING a transition back to GONE should never show the lockscreen, even though the current
+ * state during the AOD/isAsleep -> GONE transition is AOD (where lockscreen visibility = true).
+ */
+ @Test
+ fun testLockscreenVisibility_falseDuringTransitionToGone_fromCanceledGone() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockscreenVisibility)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ // Not visible since we're GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ // Remains not visible from GONE -> AOD (canceled) -> AOD since we never
+ // FINISHED in AOD, and special-case handling for the insecure camera launch
+ // ensures that we use the lockscreen visibility for GONE (false) if we're
+ // STARTED to GONE after a CANCELED from GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // Make sure there's no stuck overrides or something - we should make lockscreen
+ // visible again once we're finished in LOCKSCREEN.
+ true,
+ ),
+ values
+ )
+ }
+
+ /** */
+ @Test
+ fun testLockscreenVisibility_trueDuringTransitionToGone_fromNotCanceledGone() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockscreenVisibility)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ // Not visible when finished in GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ // Still not visible during GONE -> AOD.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // Visible now that we're FINISHED in AOD.
+ true
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // Remains visible from AOD during transition.
+ true
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ false,
+ true,
+ // Until we're finished in GONE again.
+ false
+ ),
+ values
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 02bd810a43d5..4bb0d4781376 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -27,11 +27,14 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel.Companion.UNLOCKED_DELAY_MS
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -71,7 +74,10 @@ class DeviceEntryIconViewModelTest : SysuiTestCase() {
fun isLongPressEnabled_unlocked() =
testScope.runTest {
val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled)
+ fingerprintPropertyRepository.supportsUdfps()
keyguardRepository.setKeyguardDismissible(true)
+ advanceTimeBy(UNLOCKED_DELAY_MS * 2) // wait for unlocked delay
+ runCurrent()
assertThat(isLongPressEnabled).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 2ec2fe3d0eb7..729086388a4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -54,6 +54,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -218,6 +219,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 1f14afa1d00b..bcec6109faf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -280,6 +280,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
+ shadeInteractor = shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
@@ -643,7 +644,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
val testConfig =
TestConfig(
- isVisible = true,
+ isVisible = false,
isClickable = false,
icon = mock(),
canShowWhileLocked = false,
@@ -673,7 +674,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
val testConfig =
TestConfig(
- isVisible = true,
+ isVisible = false,
isClickable = false,
icon = mock(),
canShowWhileLocked = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 45f49f01a43e..29820f7a7249 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -28,7 +28,7 @@ import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -514,7 +514,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
kosmos.setCommunalAvailable(true)
runCurrent()
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
@@ -526,7 +526,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
)
clearInvocations(mediaCarouselController)
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ communalInteractor.onSceneChanged(CommunalScenes.Blank)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
@@ -549,7 +549,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
// UMO goes to communal even over the lock screen.
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
@@ -571,7 +571,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
// Device is on lock screen.
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
new file mode 100644
index 000000000000..ac4107359dbc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection.appselector.view
+
+import android.app.ActivityOptions
+import android.app.IActivityTaskManager
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Expect
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+
+@SmallTest
+class MediaProjectionRecentsViewControllerTest : SysuiTestCase() {
+
+ @get:Rule val expect: Expect = Expect.create()
+
+ private val recentTasksAdapter = mock<RecentTasksAdapter>()
+ private val tasksAdapterFactory = RecentTasksAdapter.Factory { _, _ -> recentTasksAdapter }
+ private val taskViewSizeProvider = mock<TaskPreviewSizeProvider>()
+ private val activityTaskManager = mock<IActivityTaskManager>()
+ private val resultHandler = mock<MediaProjectionAppSelectorResultHandler>()
+ private val bundleCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+
+ private val task =
+ RecentTask(
+ taskId = 123,
+ displayId = 456,
+ userId = 789,
+ topActivityComponent = null,
+ baseIntentComponent = null,
+ colorBackground = null,
+ isForegroundTask = false
+ )
+
+ private val taskView =
+ View(context).apply {
+ layoutParams = ViewGroup.LayoutParams(/* width = */ 100, /* height = */ 200)
+ }
+
+ private val controller =
+ MediaProjectionRecentsViewController(
+ tasksAdapterFactory,
+ taskViewSizeProvider,
+ activityTaskManager,
+ resultHandler
+ )
+
+ @Test
+ fun onRecentAppClicked_taskWithSameIdIsStartedFromRecents() {
+ controller.onRecentAppClicked(task, taskView)
+
+ verify(activityTaskManager).startActivityFromRecents(eq(task.taskId), any())
+ }
+
+ @Test
+ fun onRecentAppClicked_launchDisplayIdIsSet() {
+ controller.onRecentAppClicked(task, taskView)
+
+ assertThat(getStartedTaskActivityOptions().launchDisplayId).isEqualTo(task.displayId)
+ }
+
+ @Test
+ fun onRecentAppClicked_taskNotInForeground_usesScaleUpAnimation() {
+ controller.onRecentAppClicked(task, taskView)
+
+ assertThat(getStartedTaskActivityOptions().animationType)
+ .isEqualTo(ActivityOptions.ANIM_SCALE_UP)
+ }
+
+ @Test
+ fun onRecentAppClicked_taskInForeground_flagOff_usesScaleUpAnimation() {
+ mSetFlagsRule.disableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
+
+ controller.onRecentAppClicked(task, taskView)
+
+ assertThat(getStartedTaskActivityOptions().animationType)
+ .isEqualTo(ActivityOptions.ANIM_SCALE_UP)
+ }
+
+ @Test
+ fun onRecentAppClicked_taskInForeground_flagOn_usesDefaultAnimation() {
+ mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
+ val foregroundTask = task.copy(isForegroundTask = true)
+
+ controller.onRecentAppClicked(foregroundTask, taskView)
+
+ expect
+ .that(getStartedTaskActivityOptions().animationType)
+ .isEqualTo(ActivityOptions.ANIM_CUSTOM)
+ expect.that(getStartedTaskActivityOptions().overrideTaskTransition).isTrue()
+ expect
+ .that(getStartedTaskActivityOptions().customExitResId)
+ .isEqualTo(com.android.internal.R.anim.resolver_close_anim)
+ expect.that(getStartedTaskActivityOptions().customEnterResId).isEqualTo(0)
+ }
+
+ private fun getStartedTaskActivityOptions(): ActivityOptions {
+ verify(activityTaskManager)
+ .startActivityFromRecents(eq(task.taskId), bundleCaptor.capture())
+ return ActivityOptions.fromBundle(bundleCaptor.value)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
index 83932b0a6133..bda0e1ed5c46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
@@ -21,32 +21,38 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createTask
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createToken
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken
+import com.android.systemui.mediaprojection.taskswitcher.activityTaskManagerTasksRepository
+import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidTestingRunner::class)
@SmallTest
class ActivityTaskManagerTasksRepositoryTest : SysuiTestCase() {
- private val fakeActivityTaskManager = FakeActivityTaskManager()
+ private val kosmos = taskSwitcherKosmos()
+ private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager
+ private val testScope = kosmos.testScope
+ private val repo = kosmos.activityTaskManagerTasksRepository
- private val dispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(dispatcher)
+ @Test
+ fun launchRecentTask_taskIsMovedToForeground() =
+ testScope.runTest {
+ val currentForegroundTask by collectLastValue(repo.foregroundTask)
+ val newForegroundTask = createTask(taskId = 1)
+ val backgroundTask = createTask(taskId = 2)
+ fakeActivityTaskManager.addRunningTasks(backgroundTask, newForegroundTask)
- private val repo =
- ActivityTaskManagerTasksRepository(
- activityTaskManager = fakeActivityTaskManager.activityTaskManager,
- applicationScope = testScope.backgroundScope,
- backgroundDispatcher = dispatcher
- )
+ repo.launchRecentTask(newForegroundTask)
+
+ assertThat(currentForegroundTask).isEqualTo(newForegroundTask)
+ }
@Test
fun findRunningTaskFromWindowContainerToken_noMatch_returnsNull() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
index 7bd97ce2670c..6043ede66b31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -17,55 +17,52 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
import android.os.Binder
-import android.os.Handler
import android.testing.AndroidTestingRunner
import android.view.ContentRecordingSession
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken
import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createTask
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createToken
+import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
+import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
+import com.android.systemui.mediaprojection.taskswitcher.mediaProjectionManagerRepository
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidTestingRunner::class)
@SmallTest
class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
+ private val kosmos = taskSwitcherKosmos()
+ private val testScope = kosmos.testScope
- private val fakeMediaProjectionManager = FakeMediaProjectionManager()
- private val fakeActivityTaskManager = FakeActivityTaskManager()
+ private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
+ private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager
- private val tasksRepo =
- ActivityTaskManagerTasksRepository(
- activityTaskManager = fakeActivityTaskManager.activityTaskManager,
- applicationScope = testScope.backgroundScope,
- backgroundDispatcher = dispatcher
- )
+ private val repo = kosmos.mediaProjectionManagerRepository
- private val repo =
- MediaProjectionManagerRepository(
- mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
- handler = Handler.getMain(),
- applicationScope = testScope.backgroundScope,
- tasksRepository = tasksRepo
- )
+ @Test
+ fun switchProjectedTask_stateIsUpdatedWithNewTask() =
+ testScope.runTest {
+ val task = createTask(taskId = 1)
+ val state by collectLastValue(repo.mediaProjectionState)
+
+ fakeActivityTaskManager.addRunningTasks(task)
+ repo.switchProjectedTask(task)
+
+ assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task))
+ }
@Test
fun mediaProjectionState_onStart_emitsNotProjecting() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnStart()
@@ -76,7 +73,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_onStop_emitsNotProjecting() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnStop()
@@ -87,7 +83,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_onSessionSet_sessionNull_emitsNotProjecting() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnSessionSet(session = null)
@@ -98,7 +93,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_onSessionSet_contentToRecordDisplay_emitsEntireScreen() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnSessionSet(
session = ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123)
@@ -111,7 +105,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_sessionSet_taskWithToken_noMatchingRunningTask_emitsEntireScreen() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
val taskWindowContainerToken = Binder()
fakeMediaProjectionManager.dispatchOnSessionSet(
@@ -128,7 +121,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
val task = createTask(taskId = 1, token = token)
fakeActivityTaskManager.addRunningTasks(task)
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnSessionSet(
session = ContentRecordingSession.createTaskSession(token.asBinder())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
index b2ebe1bcbc8b..33e65f26716a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
@@ -17,53 +17,33 @@
package com.android.systemui.mediaprojection.taskswitcher.domain.interactor
import android.content.Intent
-import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createTask
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionManager
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionManager.Companion.createSingleTaskSession
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionManagerRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager
+import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createSingleTaskSession
import com.android.systemui.mediaprojection.taskswitcher.domain.model.TaskSwitchState
+import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
+import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherInteractor
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidTestingRunner::class)
@SmallTest
class TaskSwitchInteractorTest : SysuiTestCase() {
- private val dispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(dispatcher)
-
- private val fakeActivityTaskManager = FakeActivityTaskManager()
- private val fakeMediaProjectionManager = FakeMediaProjectionManager()
-
- private val tasksRepo =
- ActivityTaskManagerTasksRepository(
- activityTaskManager = fakeActivityTaskManager.activityTaskManager,
- applicationScope = testScope.backgroundScope,
- backgroundDispatcher = dispatcher
- )
-
- private val mediaRepo =
- MediaProjectionManagerRepository(
- mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
- handler = Handler.getMain(),
- applicationScope = testScope.backgroundScope,
- tasksRepository = tasksRepo,
- )
-
- private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo)
+ private val kosmos = taskSwitcherKosmos()
+ private val testScope = kosmos.testScope
+ private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager
+ private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
+ private val interactor = kosmos.taskSwitcherInteractor
@Test
fun taskSwitchChanges_notProjecting_foregroundTaskChange_emitsNotProjectingTask() =
@@ -118,6 +98,40 @@ class TaskSwitchInteractorTest : SysuiTestCase() {
}
@Test
+ fun taskSwitchChanges_projectingTask_foregroundTaskDifferent_thenSwitched_emitsUnchanged() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 0)
+ val foregroundTask = createTask(taskId = 1)
+ val taskSwitchState by collectLastValue(interactor.taskSwitchChanges)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(token = projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ interactor.switchProjectedTask(foregroundTask)
+
+ assertThat(taskSwitchState).isEqualTo(TaskSwitchState.TaskUnchanged)
+ }
+
+ @Test
+ fun taskSwitchChanges_projectingTask_foregroundTaskDifferent_thenWentBack_emitsUnchanged() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 0)
+ val foregroundTask = createTask(taskId = 1)
+ val taskSwitchState by collectLastValue(interactor.taskSwitchChanges)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(token = projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ interactor.goBackToTask(projectedTask)
+
+ assertThat(taskSwitchState).isEqualTo(TaskSwitchState.TaskUnchanged)
+ }
+
+ @Test
fun taskSwitchChanges_projectingTask_foregroundTaskLauncher_emitsTaskUnchanged() =
testScope.runTest {
val projectedTask = createTask(taskId = 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
index d0c6d7cddc46..9382c5882b25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
@@ -18,74 +18,53 @@ package com.android.systemui.mediaprojection.taskswitcher.ui
import android.app.Notification
import android.app.NotificationManager
-import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionManager
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionManagerRepository
-import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
-import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager
+import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
+import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherViewModel
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidTestingRunner::class)
@SmallTest
class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
- private val notificationManager: NotificationManager = mock()
-
- private val dispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(dispatcher)
-
- private val fakeActivityTaskManager = FakeActivityTaskManager()
- private val fakeMediaProjectionManager = FakeMediaProjectionManager()
-
- private val tasksRepo =
- ActivityTaskManagerTasksRepository(
- activityTaskManager = fakeActivityTaskManager.activityTaskManager,
- applicationScope = testScope.backgroundScope,
- backgroundDispatcher = dispatcher
- )
-
- private val mediaRepo =
- MediaProjectionManagerRepository(
- mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
- handler = Handler.getMain(),
- applicationScope = testScope.backgroundScope,
- tasksRepository = tasksRepo,
- )
-
- private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo)
- private val viewModel = TaskSwitcherNotificationViewModel(interactor)
+ private val notificationManager = mock<NotificationManager>()
+ private val kosmos = taskSwitcherKosmos()
+ private val testScope = kosmos.testScope
+ private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager
+ private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
+ private val viewModel = kosmos.taskSwitcherViewModel
- private val coordinator =
- TaskSwitcherNotificationCoordinator(
- context,
- notificationManager,
- testScope.backgroundScope,
- dispatcher,
- viewModel
- )
+ private lateinit var coordinator: TaskSwitcherNotificationCoordinator
@Before
fun setup() {
+ coordinator =
+ TaskSwitcherNotificationCoordinator(
+ context,
+ notificationManager,
+ testScope.backgroundScope,
+ viewModel,
+ fakeBroadcastDispatcher,
+ )
coordinator.start()
}
@@ -105,7 +84,7 @@ class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
testScope.runTest {
fakeMediaProjectionManager.dispatchOnStop()
- verify(notificationManager).cancel(any())
+ verify(notificationManager).cancel(any(), any())
}
}
@@ -114,7 +93,7 @@ class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
testScope.runTest {
fakeMediaProjectionManager.dispatchOnStop()
val idCancel = argumentCaptor<Int>()
- verify(notificationManager).cancel(idCancel.capture())
+ verify(notificationManager).cancel(any(), idCancel.capture())
switchTask()
val idNotify = argumentCaptor<Int>()
@@ -124,9 +103,55 @@ class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
}
}
+ @Test
+ fun switchTaskAction_hidesNotification() =
+ testScope.runTest {
+ switchTask()
+ val notification = argumentCaptor<Notification>()
+ verify(notificationManager).notify(any(), any(), notification.capture())
+ verify(notificationManager, never()).cancel(any(), any())
+
+ val action = findSwitchAction(notification.value)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ action.actionIntent.intent
+ )
+
+ verify(notificationManager).cancel(any(), any())
+ }
+
+ @Test
+ fun goBackAction_hidesNotification() =
+ testScope.runTest {
+ switchTask()
+ val notification = argumentCaptor<Notification>()
+ verify(notificationManager).notify(any(), any(), notification.capture())
+ verify(notificationManager, never()).cancel(any(), any())
+
+ val action = findGoBackAction(notification.value)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ action.actionIntent.intent
+ )
+
+ verify(notificationManager).cancel(any(), any())
+ }
+
+ private fun findSwitchAction(notification: Notification): Notification.Action {
+ return notification.actions.first {
+ it.title == context.getString(R.string.media_projection_task_switcher_action_switch)
+ }
+ }
+
+ private fun findGoBackAction(notification: Notification): Notification.Action {
+ return notification.actions.first {
+ it.title == context.getString(R.string.media_projection_task_switcher_action_back)
+ }
+ }
+
private fun switchTask() {
- val projectedTask = FakeActivityTaskManager.createTask(taskId = 1)
- val foregroundTask = FakeActivityTaskManager.createTask(taskId = 2)
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
fakeMediaProjectionManager.dispatchOnSessionSet(
session =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
index 7d38de4bc2d3..a468953b971e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
@@ -17,57 +17,35 @@
package com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel
import android.content.Intent
-import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createTask
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionManager
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionManager.Companion.createDisplaySession
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionManager.Companion.createSingleTaskSession
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionManagerRepository
-import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createDisplaySession
+import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createSingleTaskSession
+import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
+import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
+import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherViewModel
import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState
+import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel.Companion.NOTIFICATION_MAX_SHOW_DURATION
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidTestingRunner::class)
@SmallTest
class TaskSwitcherNotificationViewModelTest : SysuiTestCase() {
- private val dispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(dispatcher)
-
- private val fakeActivityTaskManager = FakeActivityTaskManager()
- private val fakeMediaProjectionManager = FakeMediaProjectionManager()
-
- private val tasksRepo =
- ActivityTaskManagerTasksRepository(
- activityTaskManager = fakeActivityTaskManager.activityTaskManager,
- applicationScope = testScope.backgroundScope,
- backgroundDispatcher = dispatcher
- )
-
- private val mediaRepo =
- MediaProjectionManagerRepository(
- mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
- handler = Handler.getMain(),
- applicationScope = testScope.backgroundScope,
- tasksRepository = tasksRepo,
- )
-
- private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo)
-
- private val viewModel = TaskSwitcherNotificationViewModel(interactor)
+ private val kosmos = taskSwitcherKosmos()
+ private val testScope = kosmos.testScope
+ private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager
+ private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
+ private val viewModel = kosmos.taskSwitcherViewModel
@Test
fun uiState_notProjecting_emitsNotShowing() =
@@ -135,6 +113,75 @@ class TaskSwitcherNotificationViewModelTest : SysuiTestCase() {
}
@Test
+ fun uiState_taskChanged_beforeDelayLimit_stillEmitsShowing() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
+ val uiState by collectLastValue(viewModel.uiState)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+
+ testScheduler.advanceTimeBy(NOTIFICATION_MAX_SHOW_DURATION - 1.milliseconds)
+ assertThat(uiState)
+ .isEqualTo(TaskSwitcherNotificationUiState.Showing(projectedTask, foregroundTask))
+ }
+
+ @Test
+ fun uiState_taskChanged_afterDelayLimit_emitsNotShowing() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
+ val uiState by collectLastValue(viewModel.uiState)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+
+ testScheduler.advanceTimeBy(NOTIFICATION_MAX_SHOW_DURATION)
+ assertThat(uiState).isEqualTo(TaskSwitcherNotificationUiState.NotShowing)
+ }
+
+ @Test
+ fun uiState_projectingTask_foregroundTaskChanged_thenTaskSwitched_emitsNotShowing() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
+ val uiState by collectLastValue(viewModel.uiState)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ viewModel.onSwitchTaskClicked(foregroundTask)
+
+ assertThat(uiState).isEqualTo(TaskSwitcherNotificationUiState.NotShowing)
+ }
+
+ @Test
+ fun uiState_projectingTask_foregroundTaskChanged_thenGoBack_emitsNotShowing() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
+ val uiState by collectLastValue(viewModel.uiState)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ viewModel.onGoBackToTaskClicked(projectedTask)
+
+ assertThat(uiState).isEqualTo(TaskSwitcherNotificationUiState.NotShowing)
+ }
+
+ @Test
fun uiState_projectingTask_foregroundTaskChanged_same_emitsNotShowing() =
testScope.runTest {
val projectedTask = createTask(taskId = 1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index 52859cdeb406..d405df7c2cba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -43,13 +43,11 @@ import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
@@ -61,7 +59,9 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -76,7 +76,6 @@ import java.util.Optional;
/** atest NavigationBarControllerTest */
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
@SmallTest
public class NavigationBarControllerImplTest extends SysuiTestCase {
@@ -88,6 +87,8 @@ public class NavigationBarControllerImplTest extends SysuiTestCase {
private StaticMockitoSession mMockitoSession;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
@Mock
private CommandQueue mCommandQueue;
@Mock
@@ -104,7 +105,7 @@ public class NavigationBarControllerImplTest extends SysuiTestCase {
mock(NavigationModeController.class),
mock(SysUiState.class),
mCommandQueue,
- Dependency.get(Dependency.MAIN_HANDLER),
+ mExecutor,
mock(ConfigurationController.class),
mock(NavBarHelper.class),
mTaskbarDelegate,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 7b285abe83ce..ada93db537e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -33,6 +33,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.SessionCreationSource
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate
import com.android.systemui.model.SysUiState
import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
@@ -41,18 +42,18 @@ import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
-import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -74,12 +75,16 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var userFileManager: UserFileManager
@Mock private lateinit var sharedPreferences: SharedPreferences
+ @Mock private lateinit var screenCaptureDisabledDialogDelegate:
+ ScreenCaptureDisabledDialogDelegate
+ @Mock private lateinit var screenCaptureDisabledDialog: SystemUIDialog
@Mock private lateinit var sysuiState: SysUiState
@Mock private lateinit var systemUIDialogManager: SystemUIDialogManager
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock private lateinit var bgExecutor: Executor
- @Mock private lateinit var mainExecutor: Executor
+ private val systemClock = FakeSystemClock()
+ private val bgExecutor = FakeExecutor(systemClock)
+ private val mainExecutor = FakeExecutor(systemClock)
@Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
private lateinit var dialog: SystemUIDialog
@@ -92,6 +97,8 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
whenever(dprLazy.get()).thenReturn(devicePolicyResolver)
whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
whenever(userContextProvider.userContext).thenReturn(mContext)
+ whenever(screenCaptureDisabledDialogDelegate.createDialog())
+ .thenReturn(screenCaptureDisabledDialog)
whenever(
userFileManager.getSharedPreferences(
eq(RecordIssueTile.TILE_SPEC),
@@ -124,6 +131,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
dprLazy,
mediaProjectionMetricsLogger,
userFileManager,
+ screenCaptureDisabledDialogDelegate,
) {
latch.countDown()
}
@@ -163,13 +171,8 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
screenRecordSwitch.isChecked = true
- val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(bgExecutor).execute(bgCaptor.capture())
- bgCaptor.value.run()
-
- val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(mainExecutor).execute(mainCaptor.capture())
- mainCaptor.value.run()
+ bgExecutor.runAllReady()
+ mainExecutor.runAllReady()
verify(mediaProjectionMetricsLogger, never())
.notifyProjectionInitiated(
@@ -192,13 +195,8 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
screenRecordSwitch.isChecked = true
- val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(bgExecutor).execute(bgCaptor.capture())
- bgCaptor.value.run()
-
- val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(mainExecutor).execute(mainCaptor.capture())
- mainCaptor.value.run()
+ bgExecutor.runAllReady()
+ mainExecutor.runAllReady()
verify(mediaProjectionMetricsLogger)
.notifyProjectionInitiated(
@@ -219,9 +217,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
screenRecordSwitch.isChecked = true
- val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(bgExecutor).execute(bgCaptor.capture())
- bgCaptor.value.run()
+ bgExecutor.runAllReady()
verify(mediaProjectionMetricsLogger)
.notifyProjectionInitiated(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 6cbe8c9a939b..b3df12ee2c10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -17,13 +17,11 @@
package com.android.systemui.screenrecord;
import static android.os.Process.myUid;
-
import static com.google.common.truth.Truth.assertThat;
-
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -48,10 +46,9 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
-import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DialogDelegate;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -84,8 +81,6 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private UserContextProvider mUserContextProvider;
- @Mock
private ScreenCaptureDevicePolicyResolver mDevicePolicyResolver;
@Mock
private DialogTransitionAnimator mDialogTransitionAnimator;
@@ -96,6 +91,22 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
+ @Mock
+ private ScreenCaptureDisabledDialogDelegate mScreenCaptureDisabledDialogDelegate;
+ @Mock
+ private SystemUIDialog mScreenCaptureDisabledDialog;
+ @Mock
+ private ScreenRecordDialogDelegate.Factory mScreenRecordDialogFactory;
+ @Mock
+ private ScreenRecordDialogDelegate mScreenRecordDialogDelegate;
+ @Mock
+ private ScreenRecordPermissionDialogDelegate.Factory
+ mScreenRecordPermissionDialogDelegateFactory;
+ @Mock
+ private ScreenRecordPermissionDialogDelegate mScreenRecordPermissionDialogDelegate;
+ @Mock
+ private SystemUIDialog mScreenRecordSystemUIDialog;
+
private FakeFeatureFlags mFeatureFlags;
private RecordingController mController;
private TestSystemUIDialogFactory mDialogFactory;
@@ -108,8 +119,6 @@ public class RecordingControllerTest extends SysuiTestCase {
Context spiedContext = spy(mContext);
when(spiedContext.getUserId()).thenReturn(TEST_USER_ID);
- when(mUserContextProvider.getUserContext()).thenReturn(spiedContext);
-
mDialogFactory = new TestSystemUIDialogFactory(
mContext,
Dependency.get(SystemUIDialogManager.class),
@@ -119,16 +128,26 @@ public class RecordingControllerTest extends SysuiTestCase {
);
mFeatureFlags = new FakeFeatureFlags();
+ when(mScreenCaptureDisabledDialogDelegate.createDialog())
+ .thenReturn(mScreenCaptureDisabledDialog);
+ when(mScreenRecordDialogFactory.create(any(), any()))
+ .thenReturn(mScreenRecordDialogDelegate);
+ when(mScreenRecordDialogDelegate.createDialog()).thenReturn(mScreenRecordSystemUIDialog);
+ when(mScreenRecordPermissionDialogDelegateFactory.create(any(), any(), anyInt(), any()))
+ .thenReturn(mScreenRecordPermissionDialogDelegate);
+ when(mScreenRecordPermissionDialogDelegate.createDialog())
+ .thenReturn(mScreenRecordSystemUIDialog);
mController = new RecordingController(
mMainExecutor,
mBroadcastDispatcher,
- mContext,
mFeatureFlags,
- mUserContextProvider,
() -> mDevicePolicyResolver,
mUserTracker,
mMediaProjectionMetricsLogger,
- mDialogFactory);
+ mScreenCaptureDisabledDialogDelegate,
+ mScreenRecordDialogFactory,
+ mScreenRecordPermissionDialogDelegateFactory
+ );
mController.addCallback(mCallback);
}
@@ -242,8 +261,8 @@ public class RecordingControllerTest extends SysuiTestCase {
mActivityStarter,
/* onStartRecordingClicked= */ null);
- assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog);
- assertThat(mDialogFactory.mLastDelegate)
+ assertThat(dialog).isSameInstanceAs(mScreenRecordSystemUIDialog);
+ assertThat(mScreenRecordPermissionDialogDelegate)
.isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
}
@@ -255,7 +274,7 @@ public class RecordingControllerTest extends SysuiTestCase {
Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
mDialogTransitionAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
- assertThat(dialog).isInstanceOf(ScreenRecordDialog.class);
+ assertThat(dialog).isEqualTo(mScreenRecordSystemUIDialog);
}
@Test
@@ -267,7 +286,7 @@ public class RecordingControllerTest extends SysuiTestCase {
Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
mDialogTransitionAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
- assertThat(dialog).isInstanceOf(ScreenCaptureDisabledDialog.class);
+ assertThat(dialog).isEqualTo(mScreenCaptureDisabledDialog);
}
@Test
@@ -284,8 +303,8 @@ public class RecordingControllerTest extends SysuiTestCase {
mActivityStarter,
/* onStartRecordingClicked= */ null);
- assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog);
- assertThat(mDialogFactory.mLastDelegate)
+ assertThat(dialog).isSameInstanceAs(mScreenRecordSystemUIDialog);
+ assertThat(mScreenRecordPermissionDialogDelegate)
.isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index 90ced92c7f30..6e480746a076 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -39,6 +39,7 @@ import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -58,6 +59,7 @@ import org.mockito.MockitoAnnotations
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class ScreenRecordPermissionDialogDelegateTest : SysuiTestCase() {
+ //@Mock private lateinit var dialogFactory: SystemUIDialog.Factory
@Mock private lateinit var starter: ActivityStarter
@Mock private lateinit var controller: RecordingController
@Mock private lateinit var userContextProvider: UserContextProvider
@@ -71,14 +73,17 @@ class ScreenRecordPermissionDialogDelegateTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
+
val systemUIDialogFactory =
- SystemUIDialog.Factory(
- context,
- Dependency.get(SystemUIDialogManager::class.java),
- Dependency.get(SysUiState::class.java),
- Dependency.get(BroadcastDispatcher::class.java),
- Dependency.get(DialogTransitionAnimator::class.java),
- )
+ SystemUIDialog.Factory(
+ context,
+ Dependency.get(SystemUIDialogManager::class.java),
+ Dependency.get(SysUiState::class.java),
+ Dependency.get(BroadcastDispatcher::class.java),
+ Dependency.get(DialogTransitionAnimator::class.java),
+ )
+
val delegate =
ScreenRecordPermissionDialogDelegate(
UserHandle.of(0),
@@ -88,11 +93,9 @@ class ScreenRecordPermissionDialogDelegateTest : SysuiTestCase() {
userContextProvider,
onStartRecordingClicked,
mediaProjectionMetricsLogger,
- systemUIDialogFactory
+ systemUIDialogFactory,
)
dialog = delegate.createDialog()
- delegate.onCreate(dialog, savedInstanceState = null)
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
index 2f911fffe335..92c240404b24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -22,8 +22,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import java.lang.IllegalStateException
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -31,12 +33,14 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
class ScreenshotSoundControllerTest : SysuiTestCase() {
private val soundProvider = mock<ScreenshotSoundProvider>()
private val mediaPlayer = mock<MediaPlayer>()
private val bgDispatcher = UnconfinedTestDispatcher()
private val scope = TestScope(bgDispatcher)
+
@Before
fun setup() {
whenever(soundProvider.getScreenshotSound()).thenReturn(mediaPlayer)
@@ -45,52 +49,59 @@ class ScreenshotSoundControllerTest : SysuiTestCase() {
@Test
fun init_soundLoading() {
createController()
- bgDispatcher.scheduler.runCurrent()
+ scope.advanceUntilIdle()
verify(soundProvider).getScreenshotSound()
}
@Test
- fun init_soundLoadingException_playAndReleaseDoNotThrow() = runTest {
- whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException())
+ fun init_soundLoadingException_playAndReleaseDoNotThrow() =
+ scope.runTest {
+ whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException())
- val controller = createController()
+ val controller = createController()
- controller.playCameraSound().await()
- controller.releaseScreenshotSound().await()
+ controller.playScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer, never()).start()
- verify(mediaPlayer, never()).release()
- }
+ verify(mediaPlayer, never()).start()
+ verify(mediaPlayer, never()).release()
+ }
@Test
- fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() = runTest {
- val controller = createController()
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() =
+ scope.runTest {
+ val controller = createController()
- controller.playCameraSound().await()
+ controller.playScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer).start()
- }
+ verify(mediaPlayer).start()
+ }
@Test
- fun playCameraSound_illegalStateException_doesNotThrow() = runTest {
- whenever(mediaPlayer.start()).thenThrow(IllegalStateException())
+ fun playCameraSound_illegalStateException_doesNotThrow() =
+ scope.runTest {
+ whenever(mediaPlayer.start()).thenThrow(IllegalStateException())
- val controller = createController()
- controller.playCameraSound().await()
+ val controller = createController()
+ controller.playScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer).start()
- verify(mediaPlayer).release()
- }
+ verify(mediaPlayer).start()
+ verify(mediaPlayer).release()
+ }
@Test
- fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest {
- val controller = createController()
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() =
+ scope.runTest {
+ val controller = createController()
- controller.releaseScreenshotSound().await()
+ controller.releaseScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer).release()
- }
+ verify(mediaPlayer).release()
+ }
private fun createController() =
ScreenshotSoundControllerImpl(soundProvider, scope, bgDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 665fc750c742..62d2d0efe24c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -26,6 +26,7 @@ import android.view.View
import android.view.WindowManager
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalRepository
@@ -33,9 +34,8 @@ import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
@@ -52,9 +52,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.After
import org.junit.Assert.assertThrows
-import org.junit.Assume.assumeTrue
import org.junit.Before
-import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -155,7 +153,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_communalClosed_doesNotIntercept() {
// Communal is closed.
- goToScene(CommunalSceneKey.Blank)
+ goToScene(CommunalScenes.Blank)
assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
}
@@ -163,7 +161,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_openGesture_interceptsTouches() {
// Communal is closed.
- goToScene(CommunalSceneKey.Blank)
+ goToScene(CommunalScenes.Blank)
// Initial touch down is intercepted, and so are touches outside of the region, until an
// up event is received.
@@ -176,7 +174,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_communalOpen_interceptsTouches() {
// Communal is open.
- goToScene(CommunalSceneKey.Communal)
+ goToScene(CommunalScenes.Communal)
// Touch events are intercepted outside of any gesture areas.
assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
@@ -187,7 +185,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_topSwipeWhenCommunalOpen_doesNotIntercept() {
// Communal is open.
- goToScene(CommunalSceneKey.Communal)
+ goToScene(CommunalScenes.Communal)
// Touch event in the top swipe reqgion is not intercepted.
assertThat(underTest.onTouchEvent(DOWN_IN_TOP_SWIPE_REGION_EVENT)).isFalse()
@@ -196,7 +194,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_bottomSwipeWhenCommunalOpen_doesNotIntercept() {
// Communal is open.
- goToScene(CommunalSceneKey.Communal)
+ goToScene(CommunalScenes.Communal)
// Touch event in the bottom swipe reqgion is not intercepted.
assertThat(underTest.onTouchEvent(DOWN_IN_BOTTOM_SWIPE_REGION_EVENT)).isFalse()
@@ -205,7 +203,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() {
// Communal is open.
- goToScene(CommunalSceneKey.Communal)
+ goToScene(CommunalScenes.Communal)
// Bouncer is visible.
bouncerShowingFlow.value = true
@@ -220,7 +218,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() {
// Communal is open.
- goToScene(CommunalSceneKey.Communal)
+ goToScene(CommunalScenes.Communal)
shadeShowingFlow.value = true
testableLooper.processAllMessages()
@@ -232,7 +230,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
@Test
fun onTouchEvent_containerViewDisposed_doesNotIntercept() {
// Communal is open.
- goToScene(CommunalSceneKey.Communal)
+ goToScene(CommunalScenes.Communal)
// Touch events are intercepted.
assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
@@ -265,7 +263,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
wm.updateViewLayout(parentView, lp)
}
- private fun goToScene(scene: CommunalSceneKey) {
+ private fun goToScene(scene: SceneKey) {
communalRepository.setDesiredScene(scene)
testableLooper.processAllMessages()
}
@@ -305,13 +303,5 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, CONTAINER_HEIGHT.toFloat(), 0)
private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
private val UP_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-
- @BeforeClass
- @JvmStatic
- fun beforeClass() {
- // Glanceable hub requires Compose, no point running any of these tests if compose isn't
- // enabled.
- assumeTrue(ComposeFacade.isComposeAvailable())
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index fd7b1399d03f..acbf9976873a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -287,7 +287,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardMediaController mKeyguardMediaController;
@Mock protected NavigationModeController mNavigationModeController;
@Mock protected NavigationBarController mNavigationBarController;
- @Mock protected QuickSettingsController mQsController;
+ @Mock protected QuickSettingsControllerImpl mQsController;
@Mock protected ShadeHeaderController mShadeHeaderController;
@Mock protected ContentResolver mContentResolver;
@Mock protected TapAgainViewController mTapAgainViewController;
@@ -380,7 +380,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
protected final ShadeExpansionStateManager mShadeExpansionStateManager =
new ShadeExpansionStateManager();
- protected QuickSettingsController mQuickSettingsController;
+ protected QuickSettingsControllerImpl mQuickSettingsController;
@Mock protected Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy;
protected FragmentHostManager.FragmentListener mFragmentListener;
@@ -794,7 +794,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(mNotificationPanelViewControllerLazy.get())
.thenReturn(mNotificationPanelViewController);
- mQuickSettingsController = new QuickSettingsController(
+ mQuickSettingsController = new QuickSettingsControllerImpl(
mNotificationPanelViewControllerLazy,
mView,
mQsFrameTranslateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index b426d1de0b00..617b25d97eee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import org.mockito.Mockito.`when` as whenever
import android.content.Context
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -33,7 +34,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.binder.BouncerViewBinder
import com.android.systemui.classifier.FalsingCollectorFake
-import com.android.systemui.compose.ComposeFacade.isComposeAvailable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlagsClassic
@@ -88,7 +88,6 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -113,7 +112,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
- @Mock private lateinit var quickSettingsController: QuickSettingsController
+ @Mock private lateinit var quickSettingsController: QuickSettingsControllerImpl
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock private lateinit var lockIconViewController: LockIconViewController
@@ -511,10 +510,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Test
@Ignore("b/321332798")
fun setsUpCommunalHubLayout_whenFlagEnabled() {
- if (!isComposeAvailable()) {
- return
- }
-
whenever(mGlanceableHubContainerController.communalAvailable())
.thenReturn(MutableStateFlow(true))
@@ -537,10 +532,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Test
fun doesNotSetupCommunalHubLayout_whenFlagDisabled() {
- if (!isComposeAvailable()) {
- return
- }
-
whenever(mGlanceableHubContainerController.communalAvailable())
.thenReturn(MutableStateFlow(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 0b49a954e848..4809a47709c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -109,7 +109,7 @@ import org.mockito.MockitoAnnotations;
import kotlinx.coroutines.test.TestScope;
-public class QuickSettingsControllerBaseTest extends SysuiTestCase {
+public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
protected static final float QS_FRAME_START_X = 0f;
protected static final int QS_FRAME_WIDTH = 1000;
protected static final int QS_FRAME_TOP = 0;
@@ -119,7 +119,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
protected static final int DEFAULT_MIN_HEIGHT_SPLIT_SHADE = DEFAULT_HEIGHT;
protected static final int DEFAULT_MIN_HEIGHT = 300;
- protected QuickSettingsController mQsController;
+ protected QuickSettingsControllerImpl mQsController;
protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
protected TestScope mTestScope = mKosmos.getTestScope();
@@ -304,7 +304,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
mMainHandler = new Handler(Looper.getMainLooper());
- mQsController = new QuickSettingsController(
+ mQsController = new QuickSettingsControllerImpl(
mPanelViewControllerLazy,
mPanelView,
mQsFrameTranslateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
index 997e0e27ef9c..b16f41234656 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
@@ -41,8 +41,8 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.res.R;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,7 +53,7 @@ import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class QuickSettingsControllerTest extends QuickSettingsControllerBaseTest {
+public class QuickSettingsControllerImplTest extends QuickSettingsControllerImplBaseTest {
@Test
public void testCloseQsSideEffects() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
index cc4a06341cc6..2c453a711c87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
@@ -27,7 +27,7 @@ import org.junit.Test
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
-class QuickSettingsControllerWithCoroutinesTest : QuickSettingsControllerBaseTest() {
+class QuickSettingsControllerImplWithCoroutinesTest : QuickSettingsControllerImplBaseTest() {
@Test
fun isExpansionEnabled_dozing_false() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
index 651006dfc953..2f957b09467b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
@@ -5,6 +5,8 @@ package com.android.systemui.shade.transition
import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -20,8 +22,7 @@ import com.android.systemui.scene.domain.interactor.PanelExpansionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.FakeSceneDataSource
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.shade.ShadeExpansionChangeEvent
@@ -117,13 +118,13 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
setUnlocked(true)
val transitionState =
MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(SceneKey.Gone)
+ ObservableTransitionState.Idle(Scenes.Gone)
)
sceneInteractor.setTransitionState(transitionState)
- changeScene(SceneKey.Gone, transitionState)
+ changeScene(Scenes.Gone, transitionState)
val currentScene by collectLastValue(sceneInteractor.currentScene)
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
assertThat(latestChangeEvent)
.isEqualTo(
@@ -135,7 +136,7 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
)
)
- changeScene(SceneKey.Shade, transitionState) { progress ->
+ changeScene(Scenes.Shade, transitionState) { progress ->
assertThat(latestChangeEvent)
.isEqualTo(
ShadeExpansionChangeEvent(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index aa53558e858d..1504d4c1f033 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -92,6 +92,21 @@ import java.util.Set;
@TestableLooper.RunWithLooper
public class KeyguardIndicationControllerTest extends KeyguardIndicationControllerBaseTest {
@Test
+ public void afterFaceLockout_skipShowingFaceNotRecognized() {
+ createController();
+ onFaceLockoutError("lockout");
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "lockout");
+ clearInvocations(mRotateTextViewController);
+
+ // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED (face fail)
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+ "Face not recognized",
+ BiometricSourceType.FACE);
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); // no updated message
+ }
+
+ @Test
public void createController_setIndicationAreaAgain_destroysPreviousRotateTextViewController() {
// GIVEN a controller with a mocked rotate text view controlller
final KeyguardIndicationRotateTextViewController mockedRotateTextViewController =
@@ -593,7 +608,7 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll
mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any(), any());
}
@Test
@@ -608,7 +623,8 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll
mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(
+ eq(message), any(), any());
}
@Test
@@ -1242,7 +1258,7 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll
public void onBiometricFailed_resetFaceHelpMessageDeferral() {
createController();
- // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+ // WHEN face sends an onBiometricAuthFailed
mKeyguardUpdateMonitorCallback.onBiometricAuthFailed(BiometricSourceType.FACE);
// THEN face help message deferral is reset
@@ -1331,7 +1347,9 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll
verify(mStatusBarKeyguardViewManager)
.setKeyguardMessage(
eq(mContext.getString(R.string.keyguard_face_unlock_unavailable)),
- any());
+ any(),
+ any()
+ );
}
@Test
@@ -1471,6 +1489,71 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll
mContext.getString(R.string.keyguard_suggest_fingerprint));
}
+ @Test
+ public void faceErrorMessageDroppedBecauseFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("lockout");
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ @Test
+ public void faceUnlockedMessageShowsEvenWhenFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
+ .thenReturn(true);
+ mController.getKeyguardCallback().onBiometricAuthenticated(0,
+ BiometricSourceType.FACE, false);
+ verifyIndicationMessage(
+ INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_successful_unlock));
+ }
+
+ @Test
+ public void onTrustAgentErrorMessageDroppedBecauseFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage");
+ verifyNoMessage(INDICATION_TYPE_TRUST);
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ @Test
+ public void trustGrantedMessageShowsEvenWhenFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called
+ final String trustGrantedMsg = "testing trust granted message after fp message";
+ mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+ false, false, new TrustGrantFlags(0), trustGrantedMsg);
+
+ // THEN verify the trust granted message shows
+ verifyIndicationMessage(
+ INDICATION_TYPE_TRUST,
+ trustGrantedMsg);
+ }
+
private void screenIsTurningOn() {
when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_TURNING_ON);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index fe16347fa298..dfbb6ea08f82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -54,7 +54,6 @@ import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -87,6 +86,7 @@ import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
+import com.android.systemui.scene.shared.model.Scenes
import org.mockito.MockitoAnnotations
@SmallTest
@@ -313,48 +313,48 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
runCurrent()
kosmos.sceneInteractor.changeScene(
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
loggingReason = "reason"
)
runCurrent()
assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isFalse()
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
// Call start to begin hydrating based on the scene framework:
underTest.start()
- kosmos.sceneInteractor.changeScene(toScene = SceneKey.Bouncer, loggingReason = "reason")
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Bouncer, loggingReason = "reason")
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
- kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason")
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason")
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
kosmos.sceneInteractor.changeScene(
- toScene = SceneKey.QuickSettings,
+ toScene = Scenes.QuickSettings,
loggingReason = "reason"
)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.QuickSettings)
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
kosmos.sceneInteractor.changeScene(
- toScene = SceneKey.Communal,
+ toScene = Scenes.Communal,
loggingReason = "reason"
)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Communal)
+ assertThat(currentScene).isEqualTo(Scenes.Communal)
assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
kosmos.sceneInteractor.changeScene(
- toScene = SceneKey.Lockscreen,
+ toScene = Scenes.Lockscreen,
loggingReason = "reason"
)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
}
@@ -377,25 +377,25 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
)
kosmos.fakeDeviceEntryRepository.setUnlocked(true)
runCurrent()
- kosmos.sceneInteractor.changeScene(toScene = SceneKey.Gone, loggingReason = "reason")
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Gone, loggingReason = "reason")
runCurrent()
assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isTrue()
- assertThat(currentScene).isEqualTo(SceneKey.Gone)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
// Call start to begin hydrating based on the scene framework:
underTest.start()
- kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason")
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason")
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
kosmos.sceneInteractor.changeScene(
- toScene = SceneKey.QuickSettings,
+ toScene = Scenes.QuickSettings,
loggingReason = "reason"
)
runCurrent()
- assertThat(currentScene).isEqualTo(SceneKey.QuickSettings)
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 7d99d05e3f0b..457d2f09ed9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -130,6 +131,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
}
@Test
@@ -156,6 +158,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
}
@Test
@@ -196,6 +199,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
}
@Test
@@ -222,6 +226,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
}
@Test
@@ -262,6 +267,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
}
@Test
@@ -288,6 +294,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, false)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
}
@Test
@@ -329,6 +336,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
}
@Test
@@ -356,6 +364,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
}
@Test
@@ -396,6 +405,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
}
@Test
@@ -422,6 +432,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
}
@Test
@@ -462,6 +473,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
}
@Test
@@ -489,6 +501,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
}
@Test
@@ -531,6 +544,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true)
}
@Test
@@ -559,6 +573,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
+ verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false)
}
@Test
@@ -584,6 +599,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!, never()).setSensitive(any(), any())
+ verify(entry.representativeEntry!!.row!!, never()).setPublicExpanderVisible(any())
}
private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry {
@@ -591,7 +607,11 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
mock<UserHandle>().apply { whenever(identifier).thenReturn(notifUserId) }
val mockSbn: StatusBarNotification =
mock<StatusBarNotification>().apply { whenever(user).thenReturn(mockUserHandle) }
- val mockEntry = mock<NotificationEntry>().apply { whenever(sbn).thenReturn(mockSbn) }
+ val mockRow: ExpandableNotificationRow = mock<ExpandableNotificationRow>()
+ val mockEntry = mock<NotificationEntry>().apply {
+ whenever(sbn).thenReturn(mockSbn)
+ whenever(row).thenReturn(mockRow)
+ }
whenever(lockscreenUserManager.needsRedaction(mockEntry)).thenReturn(needsRedaction)
whenever(mockEntry.rowExists()).thenReturn(true)
return object : ListEntry("key", 0) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 73c49c023dd5..115a0d367e87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -46,6 +47,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
@@ -141,12 +143,14 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
fun changeIsChildInGroup_asyncHybirdFlagEnabled_needReInflation() {
// Given: an Entry that is not child in group
// AsyncHybridViewInflation flag is enabled
- whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(false)
+ val spySbn = spy(entry.sbn)
+ entry.sbn = spySbn
+ whenever(spySbn.isAppOrSystemGroupChild).thenReturn(false)
val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
assertThat(oldAdjustment.isChildInGroup).isFalse()
// When: the Entry becomes a group child
- whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(true)
+ whenever(spySbn.isAppOrSystemGroupChild).thenReturn(true)
val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
assertThat(newAdjustment.isChildInGroup).isTrue()
assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
@@ -160,12 +164,14 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
fun changeIsChildInGroup_asyncHybirdFlagDisabled_noNeedForReInflation() {
// Given: an Entry that is not child in group
// AsyncHybridViewInflation flag is disabled
- whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(false)
+ val spySbn = spy(entry.sbn)
+ entry.sbn = spySbn
+ whenever(spySbn.isAppOrSystemGroupChild).thenReturn(false)
val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
assertThat(oldAdjustment.isChildInGroup).isFalse()
// When: the Entry becomes a group child
- whenever(groupMembershipManager.isChildInGroup(entry)).thenReturn(true)
+ whenever(spySbn.isAppOrSystemGroupChild).thenReturn(true)
val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
assertThat(newAdjustment.isChildInGroup).isTrue()
assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
@@ -173,4 +179,24 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
// Then: need no re-inflation
assertFalse(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
}
+
+ @Test
+ @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
+ fun changeIsGroupSummary_needReInflation() {
+ // Given: an Entry that is not a group summary
+ val spySbn = spy(entry.sbn)
+ entry.sbn = spySbn
+ whenever(spySbn.isAppOrSystemGroupSummary).thenReturn(false)
+ val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(oldAdjustment.isGroupSummary).isFalse()
+
+ // When: the Entry becomes a group summary
+ whenever(spySbn.isAppOrSystemGroupSummary).thenReturn(true)
+ val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(newAdjustment.isGroupSummary).isTrue()
+ assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
+
+ // Then: Need re-inflation
+ assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index b3fc25c47912..24195fe0640c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -16,8 +16,10 @@
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
@@ -238,6 +240,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun animationsEnabled_isTrue_whenKeyguardIsShowing() =
testComponent.runTest {
keyguardTransitionRepository.sendTransitionStep(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 49ba9156bd33..42a6924b95e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -69,6 +69,7 @@ import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -330,6 +331,61 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME)
+ public void setHideSensitive_shouldNotDisturbAnimation() throws Exception {
+ //Given: A row that is during alpha animation
+ ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+
+ assertEquals(row.getPrivateLayout(), row.getContentView());
+ row.setContentAlpha(0.5f);
+
+ //When: Set its hideSensitive without changing the content view to show
+ row.setHideSensitive(
+ /* hideSensitive= */ false,
+ /* animated= */ false,
+ /* delay= */ 0L,
+ /* duration= */ 0L
+ );
+ assertEquals(row.getPrivateLayout(), row.getContentView());
+
+ //Then: The alpha value should not be reset
+ assertEquals(0.5f, row.getPrivateLayout().getAlpha(), 0);
+ }
+
+ @Test
+ @EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME)
+ public void setHideSensitive_changeContent_shouldNotDisturbAnimation() throws Exception {
+
+ // Given: A sensitive row that has public version but is not hiding sensitive,
+ // and is during an animation that sets its alpha value to be 0.5f
+ Notification publicNotif = mNotificationTestHelper.createNotification();
+ publicNotif.publicVersion = mNotificationTestHelper.createNotification();
+ ExpandableNotificationRow row = mNotificationTestHelper.createRow(publicNotif);
+ row.setSensitive(true, false);
+ row.setContentAlpha(0.5f);
+
+ assertEquals(0.5f, row.getPrivateLayout().getAlpha(), 0);
+ assertEquals(View.VISIBLE, row.getPrivateLayout().getVisibility());
+
+ // When: Change its hideSensitive and changes the content view to show the public version
+ row.setHideSensitive(
+ /* hideSensitive= */ true,
+ /* animated= */ false,
+ /* delay= */ 0L,
+ /* duration= */ 0L
+ );
+
+ // Then: The alpha value of private layout should be reset to 1, private layout be
+ // INVISIBLE;
+ // The alpha value of public layout should be 0.5 to preserve the animation state, public
+ // layout should be VISIBLE
+ assertEquals(View.INVISIBLE, row.getPrivateLayout().getVisibility());
+ assertEquals(1f, row.getPrivateLayout().getAlpha(), 0);
+ assertEquals(View.VISIBLE, row.getPublicLayout().getVisibility());
+ assertEquals(0.5f, row.getPublicLayout().getAlpha(), 0);
+ }
+
+ @Test
public void testReinflatedOnDensityChange() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index dbf7b6ce145e..012ff2e31562 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -38,6 +38,7 @@ import android.util.ArraySet
import android.view.View
import android.view.accessibility.accessibilityManager
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.internal.logging.metricsLogger
@@ -55,8 +56,7 @@ import com.android.systemui.scene.data.repository.WindowRootViewVisibilityReposi
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.ObservableTransitionState
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.shade.shadeControllerSceneImpl
import com.android.systemui.statusbar.NotificationEntryHelper
@@ -602,9 +602,9 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
private fun setIsLockscreenOrShadeVisible(isVisible: Boolean) {
val key =
if (isVisible) {
- SceneKey.Lockscreen
+ Scenes.Lockscreen
} else {
- SceneKey.Bouncer
+ Scenes.Bouncer
}
sceneInteractor.changeScene(key, "test")
sceneInteractor.setTransitionState(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index e78081fc34bd..fb49499fc29d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -89,6 +89,8 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
+import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.util.time.SystemClockImpl;
import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
@@ -136,6 +138,8 @@ public class NotificationTestHelper {
public final Runnable mFutureDismissalRunnable;
private @InflationFlag int mDefaultInflationFlags;
private final FakeFeatureFlags mFeatureFlags;
+ private final SystemClock mSystemClock;
+ private final RowInflaterTaskLogger mRowInflaterTaskLogger;
public NotificationTestHelper(
Context context,
@@ -199,6 +203,9 @@ public class NotificationTestHelper {
mFutureDismissalRunnable = mock(Runnable.class);
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
.thenReturn(mFutureDismissalRunnable);
+
+ mSystemClock = new SystemClockImpl();
+ mRowInflaterTaskLogger = mock(RowInflaterTaskLogger.class);
}
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
@@ -572,7 +579,8 @@ public class NotificationTestHelper {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
if (com.android.systemui.Flags.notificationRowUserContext()) {
- inflater.setFactory2(new RowInflaterTask.RowAsyncLayoutInflater(entry));
+ inflater.setFactory2(new RowInflaterTask.RowAsyncLayoutInflater(entry, mSystemClock,
+ mRowInflaterTaskLogger));
}
mRow = (ExpandableNotificationRow) inflater.inflate(
R.layout.status_bar_notification_row,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt
new file mode 100644
index 000000000000..f88bd7ec60bb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(NotificationViewFlipperPausing.FLAG_NAME)
+class NotificationViewFlipperViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ val underTest
+ get() = kosmos.notificationViewFlipperViewModel
+
+ @Test
+ fun testIsPaused_falseWhenViewingShade() =
+ kosmos.testScope.runTest {
+ val isPaused by collectLastValue(underTest.isPaused)
+
+ // WHEN shade is open
+ kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ runCurrent()
+
+ // THEN view flippers should NOT be paused
+ assertThat(isPaused).isFalse()
+ }
+
+ @Test
+ fun testIsPaused_trueWhenViewingKeyguard() =
+ kosmos.testScope.runTest {
+ val isPaused by collectLastValue(underTest.isPaused)
+
+ // WHEN on keyguard
+ kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ runCurrent()
+
+ // THEN view flippers should be paused
+ assertThat(isPaused).isTrue()
+ }
+
+ @Test
+ fun testIsPaused_trueWhenStartingToSleep() =
+ kosmos.testScope.runTest {
+ val isPaused by collectLastValue(underTest.isPaused)
+
+ // WHEN shade is open
+ kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ // AND device is starting to go to sleep
+ kosmos.fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP)
+ runCurrent()
+
+ // THEN view flippers should be paused
+ assertThat(isPaused).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index be976a1c9eaf..1f38a73020b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -16,11 +16,17 @@
package com.android.systemui.statusbar.notification.stack;
+import static org.junit.Assert.assertNull;
+
+import android.app.Notification;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
import android.view.View;
+import android.widget.RemoteViews;
import androidx.test.filters.SmallTest;
@@ -28,6 +34,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import org.junit.Assert;
@@ -40,6 +47,7 @@ import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
+//@DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
public class NotificationChildrenContainerTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
@@ -138,6 +146,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
public void testLowPriorityHeaderCleared() {
mGroup.setIsLowPriority(true);
NotificationHeaderView lowPriorityHeaderView =
@@ -145,11 +154,12 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility());
Assert.assertSame(mChildrenContainer, lowPriorityHeaderView.getParent());
mGroup.setIsLowPriority(false);
- Assert.assertNull(lowPriorityHeaderView.getParent());
- Assert.assertNull(mChildrenContainer.getLowPriorityViewWrapper());
+ assertNull(lowPriorityHeaderView.getParent());
+ assertNull(mChildrenContainer.getLowPriorityViewWrapper());
}
@Test
+ @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
public void testRecreateNotificationHeader_hasHeader() {
mChildrenContainer.recreateNotificationHeader(null, false);
Assert.assertNotNull("Children container must have a header after recreation",
@@ -157,6 +167,76 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
+ public void testSetLowPriorityWithAsyncInflation_noHeaderReInflation() {
+ mChildrenContainer.setIsLowPriority(true);
+ assertNull("We don't inflate header from the main thread with Async "
+ + "Inflation enabled", mChildrenContainer.getCurrentHeaderView());
+ }
+
+ @Test
+ @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
+ public void setLowPriorityBeforeLowPriorityHeaderSet() {
+
+ //Given: the children container does not have a low-priority header, and is not low-priority
+ assertNull(mChildrenContainer.getLowPriorityViewWrapper());
+ mGroup.setIsLowPriority(false);
+
+ //When: set the children container to be low-priority and set the low-priority header
+ mGroup.setIsLowPriority(true);
+ mGroup.setLowPriorityGroupHeader(createHeaderView(/* lowPriorityHeader= */ true));
+
+ //Then: the low-priority group header should be visible
+ NotificationHeaderView lowPriorityHeaderView =
+ mChildrenContainer.getLowPriorityViewWrapper().getNotificationHeader();
+ Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility());
+ Assert.assertSame(mChildrenContainer, lowPriorityHeaderView.getParent());
+
+ //When: set the children container to be not low-priority and set the normal header
+ mGroup.setIsLowPriority(false);
+ mGroup.setGroupHeader(createHeaderView(/* lowPriorityHeader= */ false));
+
+ //Then: the low-priority group header should not be visible , normal header should be
+ // visible
+ Assert.assertEquals(View.INVISIBLE, lowPriorityHeaderView.getVisibility());
+ Assert.assertEquals(
+ View.VISIBLE,
+ mChildrenContainer.getNotificationHeaderWrapper().getNotificationHeader()
+ .getVisibility()
+ );
+ }
+
+ @Test
+ @EnableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
+ public void changeLowPriorityAfterHeaderSet() {
+
+ //Given: the children container does not have headers, and is not low-priority
+ assertNull(mChildrenContainer.getLowPriorityViewWrapper());
+ assertNull(mChildrenContainer.getNotificationHeaderWrapper());
+ mGroup.setIsLowPriority(false);
+
+ //When: set the set the normal header
+ mGroup.setGroupHeader(createHeaderView(/* lowPriorityHeader= */ false));
+
+ //Then: the group header should be visible
+ NotificationHeaderView headerView =
+ mChildrenContainer.getNotificationHeaderWrapper().getNotificationHeader();
+ Assert.assertEquals(View.VISIBLE, headerView.getVisibility());
+ Assert.assertSame(mChildrenContainer, headerView.getParent());
+
+ //When: set the set the row to be low priority, and set the low-priority header
+ mGroup.setIsLowPriority(true);
+ mGroup.setLowPriorityGroupHeader(createHeaderView(/* lowPriorityHeader= */ true));
+
+ //Then: the header view should not be visible, the low-priority group header should be
+ // visible
+ Assert.assertEquals(View.INVISIBLE, headerView.getVisibility());
+ NotificationHeaderView lowPriorityHeaderView =
+ mChildrenContainer.getLowPriorityViewWrapper().getNotificationHeader();
+ Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility());
+ }
+
+ @Test
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_last_child() {
List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren();
ExpandableNotificationRow notificationRow = children.get(children.size() - 1);
@@ -170,6 +250,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_header() {
NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper();
Assert.assertEquals(0f, header.getTopRoundness(), 0.001f);
@@ -180,6 +261,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_headerLowPriority() {
mChildrenContainer.setIsLowPriority(true);
@@ -190,4 +272,17 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
Assert.assertEquals(1f, header.getTopRoundness(), 0.001f);
}
+
+ private NotificationHeaderView createHeaderView(boolean lowPriority) {
+ Notification notification = mNotificationTestHelper.createNotification();
+ final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
+ notification);
+ RemoteViews headerRemoteViews;
+ if (lowPriority) {
+ headerRemoteViews = builder.makeLowPriorityContentView(true);
+ } else {
+ headerRemoteViews = builder.makeNotificationGroupHeader();
+ }
+ return (NotificationHeaderView) headerRemoteViews.apply(getContext(), mChildrenContainer);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt
new file mode 100644
index 000000000000..1c6bda985a0e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.stack.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationStackInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ val underTest
+ get() = kosmos.notificationStackInteractor
+
+ @Test
+ fun testIsShowingOnLockscreen_falseWhenViewingShade() =
+ kosmos.testScope.runTest {
+ val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen)
+
+ // WHEN shade is open
+ kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ runCurrent()
+
+ // THEN notifications are not showing on lockscreen
+ assertThat(onLockscreen).isFalse()
+ }
+
+ @Test
+ fun testIsShowingOnLockscreen_trueWhenViewingKeyguard() =
+ kosmos.testScope.runTest {
+ val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen)
+
+ // WHEN on keyguard
+ kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ runCurrent()
+
+ // THEN notifications are showing on lockscreen
+ assertThat(onLockscreen).isTrue()
+ }
+
+ @Test
+ fun testIsShowingOnLockscreen_trueWhenStartingToSleep() =
+ kosmos.testScope.runTest {
+ val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen)
+
+ // WHEN shade is open
+ kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ // AND device is starting to go to sleep
+ kosmos.fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP)
+ runCurrent()
+
+ // THEN notifications are showing on lockscreen
+ assertThat(onLockscreen).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 3a94295de668..84156ee1fd53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -73,6 +73,7 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
+import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -95,8 +96,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.communal.data.repository.CommunalRepository;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
-import com.android.systemui.communal.shared.model.CommunalSceneKey;
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState;
+import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -842,16 +842,16 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void testEnteringGlanceableHub_updatesScrim() {
// Transition to the glanceable hub.
- mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle(
- CommunalSceneKey.Communal.INSTANCE)));
+ mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
+ CommunalScenes.Communal)));
mTestScope.getTestScheduler().runCurrent();
// ScrimState also transitions.
verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB);
// Transition away from the glanceable hub.
- mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle(
- CommunalSceneKey.Blank.INSTANCE)));
+ mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
+ CommunalScenes.Blank)));
mTestScope.getTestScheduler().runCurrent();
// ScrimState goes back to UNLOCKED.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 1687ccbf5826..443dd6a5dbc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -20,6 +20,7 @@ import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
import android.app.StatusBarManager.WINDOW_STATE_HIDING
import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import android.view.InputDevice
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
@@ -233,14 +234,35 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- fun shadeIsExpandedOnStatusIconClick() {
+ fun shadeIsExpandedOnStatusIconMouseClick() {
val view = createViewMock()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
controller = createAndInitController(view)
}
val statusContainer = view.requireViewById<View>(R.id.system_icons)
- statusContainer.performClick()
- verify(shadeViewController).expand(any())
+ statusContainer.dispatchTouchEvent(
+ getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
+ )
+ verify(shadeControllerImpl).animateExpandShade()
+ }
+
+ @Test
+ fun statusIconContainerIsNotHandlingTouchScreenTouches() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val statusContainer = view.requireViewById<View>(R.id.system_icons)
+ val handled = statusContainer.dispatchTouchEvent(
+ getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
+ )
+ assertThat(handled).isFalse()
+ }
+
+ private fun getActionUpEventFromSource(source: Int): MotionEvent {
+ val ev = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0f, 0f, 0)
+ ev.source = source
+ return ev
}
@Test
@@ -250,7 +272,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
controller = createAndInitController(view)
}
view.performClick()
- verify(shadeViewController, never()).expand(any())
+ verify(shadeControllerImpl, never()).animateExpandShade()
}
private fun getCommandQueueCallback(): CommandQueue.Callbacks {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 5e8b62e799c1..fd2dead02c6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -26,6 +26,7 @@ import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.PrivacyIndicatorBounds
import android.view.RoundedCorners
+import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
@@ -50,6 +51,8 @@ import org.mockito.Mockito.verify
class PhoneStatusBarViewTest : SysuiTestCase() {
private lateinit var view: PhoneStatusBarView
+ private val systemIconsContainer: View
+ get() = view.requireViewById(R.id.system_icons)
private val contentInsetsProvider = mock<StatusBarContentInsetsProvider>()
private val windowController = mock<StatusBarWindowController>()
@@ -62,6 +65,7 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
)
mDependency.injectTestDependency(DarkIconDispatcher::class.java, mock<DarkIconDispatcher>())
mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
+ context.ensureTestableResources()
view = spy(createStatusBarView())
whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
@@ -217,7 +221,7 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
val newInsets = Insets.NONE
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ .thenReturn(newInsets)
view.onConfigurationChanged(Configuration())
assertThat(view.paddingLeft).isEqualTo(previousInsets.left)
@@ -239,7 +243,7 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
val newInsets = Insets.NONE
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ .thenReturn(newInsets)
configuration.densityDpi = 456
view.onConfigurationChanged(configuration)
@@ -262,7 +266,7 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
val newInsets = Insets.NONE
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ .thenReturn(newInsets)
configuration.fontScale = 2f
view.onConfigurationChanged(configuration)
@@ -273,6 +277,19 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
}
@Test
+ fun onConfigurationChanged_systemIconsHeightChanged_containerHeightIsUpdated() {
+ val newHeight = 123456
+ context.orCreateTestableResources.addOverride(
+ R.dimen.status_bar_system_icons_height,
+ newHeight
+ )
+
+ view.onConfigurationChanged(Configuration())
+
+ assertThat(systemIconsContainer.layoutParams.height).isEqualTo(newHeight)
+ }
+
+ @Test
fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() {
val insets = Insets.of(/* left = */ 90, /* top = */ 10, /* right = */ 45, /* bottom = */ 50)
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bd7406ad004b..562aa6a4f497 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -86,6 +86,7 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
@@ -94,9 +95,11 @@ import com.android.systemui.shade.ShadeLockscreenInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.domain.interactor.StatusBarKeyguardViewManagerInteractor;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.google.common.truth.Truth;
@@ -222,7 +225,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+ () -> mock(KeyguardSurfaceBehindInteractor.class),
+ mock(JavaAdapter.class),
+ () -> mock(SceneInteractor.class),
+ mock(StatusBarKeyguardViewManagerInteractor.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -730,7 +736,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+ () -> mock(KeyguardSurfaceBehindInteractor.class),
+ mock(JavaAdapter.class),
+ () -> mock(SceneInteractor.class),
+ mock(StatusBarKeyguardViewManagerInteractor.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 41514ce3e72c..938b2f88d4b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -79,6 +79,7 @@ import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeAnimationRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -130,6 +131,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private ActivityStarter mActivityStarter;
@Mock
+ private CommandQueue mCommandQueue;
+ @Mock
private NotificationClickNotifier mClickNotifier;
@Mock
private StatusBarStateController mStatusBarStateController;
@@ -236,6 +239,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mVisibilityProvider,
headsUpManager,
mActivityStarter,
+ mCommandQueue,
mClickNotifier,
mStatusBarKeyguardViewManager,
mock(KeyguardManager.class),
@@ -257,7 +261,9 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(NotificationShadeWindowController.class),
mActivityTransitionAnimator,
new ShadeAnimationInteractorLegacyImpl(
- new ShadeAnimationRepository(), new FakeShadeRepository()),
+ new ShadeAnimationRepository(),
+ new FakeShadeRepository()
+ ),
notificationAnimationProvider,
mock(LaunchFullScreenIntentProvider.class),
mPowerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 1dafcc48f19f..b0404a055a68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -15,13 +15,14 @@
package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 1f27a289e52c..47899a66b772 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -38,9 +38,12 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.isActive
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -156,14 +159,35 @@ class MobileIconsViewModelTest : SysuiTestCase() {
val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS)
// Both impls are cached
- assertThat(underTest.mobileIconSubIdCache)
- .containsExactly(1, model1.commonImpl, 2, model2.commonImpl)
+ assertThat(underTest.reuseCache.keys).containsExactly(1, 2)
// SUB_1 is removed from the list...
interactor.filteredSubscriptions.value = listOf(SUB_2)
// ... and dropped from the cache
- assertThat(underTest.mobileIconSubIdCache).containsExactly(2, model2.commonImpl)
+ assertThat(underTest.reuseCache.keys).containsExactly(2)
+ }
+
+ @Test
+ fun caching_invalidatedViewModelsAreCanceled() =
+ testScope.runTest {
+ // Retrieve models to trigger caching
+ val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
+ val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS)
+
+ var scope1 = underTest.reuseCache[1]?.second
+ var scope2 = underTest.reuseCache[2]?.second
+
+ // Scopes are not canceled
+ assertTrue(scope1!!.isActive)
+ assertTrue(scope2!!.isActive)
+
+ // SUB_1 is removed from the list...
+ interactor.filteredSubscriptions.value = listOf(SUB_2)
+
+ // scope1 is canceled
+ assertFalse(scope1!!.isActive)
+ assertTrue(scope2!!.isActive)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index a9308601a314..fbefb0eedfa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -609,6 +609,7 @@ public class BubblesTest extends SysuiTestCase {
mSysUiState,
mFeatureFlags,
mNotifPipelineFlags,
+ syncExecutor,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -651,7 +652,7 @@ public class BubblesTest extends SysuiTestCase {
}
@Test
- public void dreamingHidesBubbles() throws RemoteException {
+ public void bubblesHiddenWhileDreaming() throws RemoteException {
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
@@ -662,7 +663,17 @@ public class BubblesTest extends SysuiTestCase {
mKeyguardStateControllerCallbackCaptor.getValue();
callback.onKeyguardShowingChanged();
+ // Dreaming should hide bubbles
assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.INVISIBLE);
+
+ // Finish dreaming should show bubbles
+ mNotificationShadeWindowController.setDreaming(false);
+ when(mIDreamManager.isDreamingOrInPreview()).thenReturn(false); // dreaming finished
+
+ // Dreaming updates come through mNotificationShadeWindowController
+ mNotificationShadeWindowController.notifyStateChangedCallbacks();
+
+ assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
index 3b5ff38e3663..1b951d95df11 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
@@ -19,6 +19,7 @@ package com.android.systemui.biometrics.data.repository
import android.util.Size
import com.android.systemui.biometrics.shared.model.DisplayRotation
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -33,6 +34,9 @@ class FakeDisplayStateRepository : DisplayStateRepository {
private val _currentDisplaySize = MutableStateFlow<Size>(Size(0, 0))
override val currentDisplaySize: StateFlow<Size> = _currentDisplaySize.asStateFlow()
+ private val _isLargeScreen = MutableStateFlow<Boolean>(false)
+ override val isLargeScreen: Flow<Boolean> = _isLargeScreen.asStateFlow()
+
override val isReverseDefaultRotation = false
fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
index f192de23fecc..c3af437dafdf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -1,6 +1,8 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.Flags
import android.hardware.biometrics.PromptInfo
+import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.shared.model.PromptKind
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -26,6 +28,9 @@ class FakePromptRepository : PromptRepository {
private val _isConfirmationRequired = MutableStateFlow(false)
override val isConfirmationRequired = _isConfirmationRequired.asStateFlow()
+ private val _showBpWithoutIconForCredential = MutableStateFlow(false)
+ override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
override val opPackageName = _opPackageName.asStateFlow()
@@ -69,6 +74,16 @@ class FakePromptRepository : PromptRepository {
_isConfirmationRequired.value = false
}
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+ val showBpForCredential =
+ Flags.customBiometricPrompt() &&
+ !Utils.isBiometricAllowed(promptInfo) &&
+ Utils.isDeviceCredentialAllowed(promptInfo) &&
+ promptInfo.contentView != null
+ _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+ }
+
fun setIsShowing(showing: Boolean) {
_isShowing.value = showing
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
index ad8ccb00f45f..5c3e1f410e63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.volume.panel
+package com.android.systemui.bouncer.shared.flag
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.media.mediaOutputDialogFactory
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-val Kosmos.mediaOutputActionsInteractor by
- Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) }
+var Kosmos.fakeComposeBouncerFlags by
+ Kosmos.Fixture { FakeComposeBouncerFlags(fakeSceneContainerFlags) }
+val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
new file mode 100644
index 000000000000..c116bbd32f9e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.shared.flag
+
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+
+class FakeComposeBouncerFlags(
+ private val sceneContainerFlags: SceneContainerFlags,
+ var composeBouncerEnabled: Boolean = false
+) : ComposeBouncerFlags {
+ override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
+ return sceneContainerFlags.isEnabled() || composeBouncerEnabled
+ }
+
+ @Deprecated(
+ "Avoid using this, this is meant to be used only by the glue code " +
+ "that includes compose bouncer in legacy keyguard.",
+ replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
+ )
+ override fun isOnlyComposeBouncerEnabled(): Boolean = composeBouncerEnabled
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index 99dfe94af3df..6d97238ba48b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -21,12 +21,12 @@ import com.android.systemui.authentication.domain.interactor.authenticationInter
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
import com.android.systemui.util.mockito.mock
@@ -42,7 +42,7 @@ val Kosmos.bouncerViewModel by Fixture {
simBouncerInteractor = simBouncerInteractor,
authenticationInteractor = authenticationInteractor,
selectedUserInteractor = selectedUserInteractor,
- flags = sceneContainerFlags,
+ flags = composeBouncerFlags,
selectedUser = userSwitcherViewModel.selectedUser,
users = userSwitcherViewModel.users,
userSwitcherMenu = userSwitcherViewModel.menu,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepository.kt
new file mode 100644
index 000000000000..b8284acc997b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.os.UserHandle
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class FakeCameraAutoRotateRepository : CameraAutoRotateRepository {
+ private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>()
+
+ /** Send a Unit signal when value changes */
+ override fun isCameraAutoRotateSettingEnabled(userHandle: UserHandle): StateFlow<Boolean> =
+ getFlow(userHandle.identifier)
+
+ fun setEnabled(userHandle: UserHandle, enabled: Boolean) {
+ getFlow(userHandle.identifier).value = enabled
+ }
+
+ /** initializes the flow if already not */
+ private fun getFlow(userId: Int): MutableStateFlow<Boolean> =
+ userMap.getOrPut(userId) { MutableStateFlow(false) }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryKosmos.kt
new file mode 100644
index 000000000000..615c59661f1d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeCameraAutoRotateRepository: FakeCameraAutoRotateRepository by
+ Kosmos.Fixture { FakeCameraAutoRotateRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepository.kt
new file mode 100644
index 000000000000..994e9b2f3683
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepository.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import android.os.UserHandle
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class FakeCameraSensorPrivacyRepository : CameraSensorPrivacyRepository {
+
+ private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>()
+ override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> =
+ getFlow(userHandle.identifier)
+
+ fun setEnabled(userHandle: UserHandle, enabled: Boolean) {
+ getFlow(userHandle.identifier).value = enabled
+ }
+
+ /** initializes the flow if already not */
+ private fun getFlow(userId: Int): MutableStateFlow<Boolean> =
+ userMap.getOrPut(userId) { MutableStateFlow(false) }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryKosmos.kt
new file mode 100644
index 000000000000..c7e704cab42f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeCameraSensorPrivacyRepository: FakeCameraSensorPrivacyRepository by
+ Kosmos.Fixture { FakeCameraSensorPrivacyRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index 9d508d23dca7..5ff588f810bd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -1,7 +1,8 @@
package com.android.systemui.communal.data.repository
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.communal.shared.model.CommunalScenes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -16,17 +17,16 @@ import kotlinx.coroutines.flow.stateIn
@OptIn(ExperimentalCoroutinesApi::class)
class FakeCommunalRepository(
applicationScope: CoroutineScope,
- override val desiredScene: MutableStateFlow<CommunalSceneKey> =
- MutableStateFlow(CommunalSceneKey.DEFAULT),
+ override val desiredScene: MutableStateFlow<SceneKey> =
+ MutableStateFlow(CommunalScenes.Default),
) : CommunalRepository {
- override fun setDesiredScene(desiredScene: CommunalSceneKey) {
+ override fun setDesiredScene(desiredScene: SceneKey) {
this.desiredScene.value = desiredScene
}
- private val defaultTransitionState =
- ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)
- private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null)
- override val transitionState: StateFlow<ObservableCommunalTransitionState> =
+ private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default)
+ private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
+ override val transitionState: StateFlow<ObservableTransitionState> =
_transitionState
.flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) }
.stateIn(
@@ -35,7 +35,7 @@ class FakeCommunalRepository(
initialValue = defaultTransitionState,
)
- override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ override fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
_transitionState.value = transitionState
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt
index c3f677e8d0d3..d5411ad77ce4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.plugins.activityStarter
@@ -40,5 +41,6 @@ val Kosmos.occludingAppDeviceEntryInteractor by
context = mockedContext,
activityStarter = activityStarter,
powerInteractor = powerInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
index 3faa6eb8f5f2..4e05de27bb33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
@@ -17,7 +17,6 @@
package com.android.systemui.flags
import android.util.Log
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import org.junit.Assert
import org.junit.Assume
@@ -42,10 +41,6 @@ class SceneContainerRule : TestRule {
null || description?.getAnnotation(EnableSceneContainer::class.java) != null
if (hasAnnotation) {
Assume.assumeTrue(
- "Compose must be available for @EnableSceneContainer test",
- ComposeFacade.isComposeAvailable()
- )
- Assume.assumeTrue(
"Couldn't set Flags.SCENE_CONTAINER_ENABLED for @EnableSceneContainer test",
trySetSceneContainerEnabled(true)
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt
index 534f773fa334..592fa3892dc9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardClockRepository.kt
@@ -45,8 +45,15 @@ class FakeKeyguardClockRepository @Inject constructor() : KeyguardClockRepositor
private val _currentClock = MutableStateFlow(null)
override val currentClock = _currentClock
- private val _previewClock = MutableStateFlow(Mockito.mock(ClockController::class.java))
- override val previewClock: StateFlow<ClockController> = _previewClock
+ private val _previewClockPair =
+ MutableStateFlow(
+ Pair(
+ Mockito.mock(ClockController::class.java),
+ Mockito.mock(ClockController::class.java)
+ )
+ )
+ override val previewClockPair: StateFlow<Pair<ClockController, ClockController>> =
+ _previewClockPair
override val clockEventController: ClockEventController
get() = mock()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index a9a2d91c0815..dcbd5777489a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.repository
import android.annotation.FloatRange
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
@@ -65,55 +66,79 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio
}
/**
- * Sends STARTED, RUNNING, and FINISHED TransitionSteps between [from] and [to], calling
- * [runCurrent] after each step.
+ * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step.
+ *
+ * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part
+ * way using [throughTransitionState].
*/
suspend fun sendTransitionSteps(
from: KeyguardState,
to: KeyguardState,
testScope: TestScope,
+ throughTransitionState: TransitionState = TransitionState.FINISHED,
) {
- sendTransitionSteps(from, to, testScope.testScheduler)
+ sendTransitionSteps(from, to, testScope.testScheduler, throughTransitionState)
}
/**
- * Sends STARTED, RUNNING, and FINISHED TransitionSteps between [from] and [to], calling
- * [runCurrent] after each step.
+ * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step.
+ *
+ * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part
+ * way using [throughTransitionState].
*/
suspend fun sendTransitionSteps(
from: KeyguardState,
to: KeyguardState,
testScheduler: TestCoroutineScheduler,
+ throughTransitionState: TransitionState = TransitionState.FINISHED,
) {
sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = from,
- to = to,
- value = 0f,
- )
+ step =
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = from,
+ to = to,
+ value = 0f,
+ )
)
testScheduler.runCurrent()
- sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.RUNNING,
- from = from,
- to = to,
- value = 0.5f
+ if (
+ throughTransitionState == TransitionState.RUNNING ||
+ throughTransitionState == TransitionState.FINISHED
+ ) {
+ sendTransitionStep(
+ step =
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = from,
+ to = to,
+ value = 0.5f
+ )
)
- )
- testScheduler.runCurrent()
+ testScheduler.runCurrent()
+ }
- sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.FINISHED,
- from = from,
- to = to,
- value = 1f,
+ if (throughTransitionState == TransitionState.FINISHED) {
+ sendTransitionStep(
+ step =
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = from,
+ to = to,
+ value = 1f,
+ )
)
+ testScheduler.runCurrent()
+ }
+ }
+
+ suspend fun sendTransitionStep(step: TransitionStep, validateStep: Boolean = true) {
+ this.sendTransitionStep(
+ step = step,
+ validateStep = validateStep,
+ ownerName = step.ownerName
)
- testScheduler.runCurrent()
}
/**
@@ -132,7 +157,22 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio
* If you're testing something involving transitions themselves and are sure you want to send
* only a FINISHED step, override [validateStep].
*/
- suspend fun sendTransitionStep(step: TransitionStep, validateStep: Boolean = true) {
+ suspend fun sendTransitionStep(
+ from: KeyguardState = KeyguardState.OFF,
+ to: KeyguardState = KeyguardState.OFF,
+ value: Float = 0f,
+ transitionState: TransitionState = TransitionState.FINISHED,
+ ownerName: String = "",
+ step: TransitionStep =
+ TransitionStep(
+ from = from,
+ to = to,
+ value = value,
+ transitionState = transitionState,
+ ownerName = ownerName
+ ),
+ validateStep: Boolean = true
+ ) {
_transitions.replayCache.last().let { lastStep ->
if (
validateStep &&
@@ -159,7 +199,9 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio
step: TransitionStep,
validateStep: Boolean = true
): Job {
- return coroutineScope.launch { sendTransitionStep(step, validateStep) }
+ return coroutineScope.launch {
+ sendTransitionStep(step = step, validateStep = validateStep)
+ }
}
suspend fun sendTransitionSteps(
@@ -168,12 +210,13 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio
validateStep: Boolean = true
) {
steps.forEach {
- sendTransitionStep(it, validateStep = validateStep)
+ sendTransitionStep(step = it, validateStep = validateStep)
testScope.testScheduler.runCurrent()
}
}
override fun startTransition(info: TransitionInfo): UUID? {
+ Log.i("TEST", "Start transition: ", Exception())
return if (info.animator == null) UUID.randomUUID() else null
}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepositoryKosmos.kt
index 8ad0a080a9dd..4c8bf9054106 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepositoryKosmos.kt
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.volume.panel.component.mediaoutput
+package com.android.systemui.keyguard.data.repository
-import dagger.Module
+import com.android.systemui.kosmos.Kosmos
-@Module interface MediaOutputModule
+val Kosmos.keyguardOcclusionRepository by Kosmos.Fixture { KeyguardOcclusionRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt
new file mode 100644
index 000000000000..530cbedbdd0c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+
+val Kosmos.fromAlternateBouncerTransitionInteractor by
+ Kosmos.Fixture {
+ FromAlternateBouncerTransitionInteractor(
+ transitionRepository = keyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ communalInteractor = communalInteractor,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index da5cd679351f..bbe37c18dd08 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -18,17 +18,21 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
val Kosmos.fromAodTransitionInteractor by
Kosmos.Fixture {
FromAodTransitionInteractor(
transitionRepository = fakeKeyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
- scope = testScope,
+ scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
new file mode 100644
index 000000000000..23dcd965c028
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+
+var Kosmos.fromDozingTransitionInteractor by
+ Kosmos.Fixture {
+ FromDozingTransitionInteractor(
+ transitionRepository = keyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ communalInteractor = communalInteractor,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt
new file mode 100644
index 000000000000..f7a9d59eac26
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+
+var Kosmos.fromDreamingLockscreenHostedTransitionInteractor by
+ Kosmos.Fixture {
+ FromDreamingLockscreenHostedTransitionInteractor(
+ transitionRepository = keyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
new file mode 100644
index 000000000000..135644cfac3e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+
+var Kosmos.fromDreamingTransitionInteractor by
+ Kosmos.Fixture {
+ FromDreamingTransitionInteractor(
+ transitionRepository = keyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ glanceableHubTransitions = glanceableHubTransitions,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
new file mode 100644
index 000000000000..1695327d75bc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+
+var Kosmos.fromGlanceableHubTransitionInteractor by
+ Kosmos.Fixture {
+ FromGlanceableHubTransitionInteractor(
+ transitionRepository = keyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ glanceableHubTransitions = glanceableHubTransitions,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index 25fc67a9691b..604d9e435e8e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -17,11 +17,13 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
val Kosmos.fromGoneTransitionInteractor by
Kosmos.Fixture {
@@ -34,5 +36,7 @@ val Kosmos.fromGoneTransitionInteractor by
keyguardInteractor = keyguardInteractor,
powerInteractor = powerInteractor,
communalInteractor = communalInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ biometricSettingsRepository = biometricSettingsRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index 3b52676fc0ef..162fd9029bb0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -23,6 +23,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
var Kosmos.fromLockscreenTransitionInteractor by
Kosmos.Fixture {
@@ -38,5 +39,6 @@ var Kosmos.fromLockscreenTransitionInteractor by
powerInteractor = powerInteractor,
glanceableHubTransitions = glanceableHubTransitions,
swipeToDismissInteractor = swipeToDismissInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
new file mode 100644
index 000000000000..fc740a180dc4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+
+val Kosmos.fromOccludedTransitionInteractor by
+ Kosmos.Fixture {
+ FromOccludedTransitionInteractor(
+ transitionRepository = keyguardTransitionRepository,
+ transitionInteractor = keyguardTransitionInteractor,
+ scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
+ communalInteractor = communalInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
index 6b764491f32a..98babffb50d3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -24,6 +24,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.user.domain.interactor.selectedUserInteractor
var Kosmos.fromPrimaryBouncerTransitionInteractor by
@@ -40,5 +41,6 @@ var Kosmos.fromPrimaryBouncerTransitionInteractor by
keyguardSecurityModel = keyguardSecurityModel,
selectedUserInteractor = selectedUserInteractor,
powerInteractor = powerInteractor,
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index 6df7493be200..6cc1e8eba73d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -16,19 +16,21 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import dagger.Lazy
val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
Kosmos.Fixture {
KeyguardTransitionInteractor(
scope = applicationCoroutineScope,
repository = keyguardTransitionRepository,
- fromLockscreenTransitionInteractor = Lazy { fromLockscreenTransitionInteractor },
- fromPrimaryBouncerTransitionInteractor =
- Lazy { fromPrimaryBouncerTransitionInteractor },
- fromAodTransitionInteractor = Lazy { fromAodTransitionInteractor },
+ keyguardRepository = keyguardRepository,
+ fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor },
+ fromPrimaryBouncerTransitionInteractor = { fromPrimaryBouncerTransitionInteractor },
+ fromAodTransitionInteractor = { fromAodTransitionInteractor },
+ fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
+ fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..8162520e5d88
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.dozingToOccludedTransitionViewModel by
+ Kosmos.Fixture {
+ DozingToOccludedTransitionViewModel(
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 8ca53e6591c0..75e3ac24e381 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
@file:OptIn(ExperimentalCoroutinesApi::class)
package com.android.systemui.keyguard.ui.viewmodel
@@ -39,8 +38,13 @@ val Kosmos.keyguardRootViewModel by Fixture {
keyguardTransitionInteractor = keyguardTransitionInteractor,
notificationsKeyguardInteractor = notificationsKeyguardInteractor,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
+ aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+ aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
+ dozingToGoneTransitionViewModel = dozingToGoneTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
+ dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 96de4bae63d4..8da5dd46b62a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.biometrics.authController
import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.splitShadeStateController
val Kosmos.lockscreenContentViewModel by
Kosmos.Fixture {
@@ -28,5 +29,6 @@ val Kosmos.lockscreenContentViewModel by
interactor = keyguardBlueprintInteractor,
authController = authController,
longPress = keyguardLongPressViewModel,
+ splitShadeStateController = splitShadeStateController,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeActivityTaskManager.kt
index 1c4870bc32b1..41d2d600e627 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeActivityTaskManager.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.mediaprojection.taskswitcher.data.repository
+package com.android.systemui.mediaprojection.taskswitcher
import android.app.ActivityManager.RunningTaskInfo
-import android.app.ActivityTaskManager
+import android.app.IActivityTaskManager
import android.app.TaskStackListener
import android.content.Intent
import android.window.IWindowContainerToken
@@ -31,7 +31,7 @@ class FakeActivityTaskManager {
private val runningTasks = mutableListOf<RunningTaskInfo>()
private val taskTaskListeners = mutableListOf<TaskStackListener>()
- val activityTaskManager = mock<ActivityTaskManager>()
+ val activityTaskManager = mock<IActivityTaskManager>()
init {
whenever(activityTaskManager.registerTaskStackListener(any())).thenAnswer {
@@ -42,10 +42,20 @@ class FakeActivityTaskManager {
taskTaskListeners -= it.arguments[0] as TaskStackListener
return@thenAnswer Unit
}
- whenever(activityTaskManager.getTasks(any())).thenAnswer {
+ whenever(activityTaskManager.getTasks(any(), any(), any(), any())).thenAnswer {
val maxNumTasks = it.arguments[0] as Int
return@thenAnswer runningTasks.take(maxNumTasks)
}
+ whenever(activityTaskManager.startActivityFromRecents(any(), any())).thenAnswer {
+ val taskId = it.arguments[0] as Int
+ val runningTask = runningTasks.find { runningTask -> runningTask.taskId == taskId }
+ if (runningTask != null) {
+ moveTaskToForeground(runningTask)
+ return@thenAnswer 0
+ } else {
+ return@thenAnswer -1
+ }
+ }
}
fun moveTaskToForeground(task: RunningTaskInfo) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeMediaProjectionManager.kt
index 44c411fdb1d1..2b6032ccafe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/FakeMediaProjectionManager.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.mediaprojection.taskswitcher.data.repository
+package com.android.systemui.mediaprojection.taskswitcher
import android.media.projection.MediaProjectionInfo
import android.media.projection.MediaProjectionManager
@@ -22,6 +22,8 @@ import android.os.Binder
import android.os.IBinder
import android.os.UserHandle
import android.view.ContentRecordingSession
+import android.window.WindowContainerToken
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -29,6 +31,7 @@ import com.android.systemui.util.mockito.whenever
class FakeMediaProjectionManager {
val mediaProjectionManager = mock<MediaProjectionManager>()
+ val helper = mock<MediaProjectionServiceHelper>()
private val callbacks = mutableListOf<MediaProjectionManager.Callback>()
@@ -41,6 +44,11 @@ class FakeMediaProjectionManager {
callbacks -= it.arguments[0] as MediaProjectionManager.Callback
return@thenAnswer Unit
}
+ whenever(helper.updateTaskRecordingSession(any())).thenAnswer {
+ val token = it.arguments[0] as WindowContainerToken
+ dispatchOnSessionSet(session = createSingleTaskSession(token.asBinder()))
+ return@thenAnswer true
+ }
}
fun dispatchOnStart(info: MediaProjectionInfo = DEFAULT_INFO) {
@@ -61,6 +69,7 @@ class FakeMediaProjectionManager {
companion object {
fun createDisplaySession(): ContentRecordingSession =
ContentRecordingSession.createDisplaySession(/* displayToMirror = */ 123)
+
fun createSingleTaskSession(token: IBinder = Binder()): ContentRecordingSession =
ContentRecordingSession.createTaskSession(token)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
new file mode 100644
index 000000000000..d344b75c9eb7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection.taskswitcher
+
+import android.os.Handler
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
+import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionManagerRepository
+import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
+import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+
+val Kosmos.fakeActivityTaskManager by Kosmos.Fixture { FakeActivityTaskManager() }
+
+val Kosmos.fakeMediaProjectionManager by Kosmos.Fixture { FakeMediaProjectionManager() }
+
+val Kosmos.activityTaskManagerTasksRepository by
+ Kosmos.Fixture {
+ ActivityTaskManagerTasksRepository(
+ activityTaskManager = fakeActivityTaskManager.activityTaskManager,
+ applicationScope = applicationCoroutineScope,
+ backgroundDispatcher = testDispatcher
+ )
+ }
+
+val Kosmos.mediaProjectionManagerRepository by
+ Kosmos.Fixture {
+ MediaProjectionManagerRepository(
+ mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
+ handler = Handler.getMain(),
+ applicationScope = applicationCoroutineScope,
+ tasksRepository = activityTaskManagerTasksRepository,
+ backgroundDispatcher = testDispatcher,
+ mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
+ )
+ }
+
+val Kosmos.taskSwitcherInteractor by
+ Kosmos.Fixture {
+ TaskSwitchInteractor(mediaProjectionManagerRepository, activityTaskManagerTasksRepository)
+ }
+
+val Kosmos.taskSwitcherViewModel by
+ Kosmos.Fixture { TaskSwitcherNotificationViewModel(taskSwitcherInteractor, testDispatcher) }
+
+@OptIn(ExperimentalCoroutinesApi::class)
+fun taskSwitcherKosmos() = Kosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/rotation/RotationLockTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/rotation/RotationLockTileKosmos.kt
new file mode 100644
index 000000000000..ecf8ce5bb8b8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/rotation/RotationLockTileKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.rotation
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.rotationlock.RotationLockNewModule
+
+val Kosmos.qsRotationLockTileConfig by
+ Kosmos.Fixture { RotationLockNewModule.provideRotationTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index 8fc419cadb21..2cdf76d50299 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -3,18 +3,18 @@ package com.android.systemui.scene
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.shared.model.SceneContainerConfig
-import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
var Kosmos.sceneKeys by Fixture {
listOf(
- SceneKey.QuickSettings,
- SceneKey.Shade,
- SceneKey.Lockscreen,
- SceneKey.Bouncer,
- SceneKey.Gone,
- SceneKey.Communal,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
+ Scenes.Gone,
+ Scenes.Communal,
)
}
-val Kosmos.initialSceneKey by Fixture { SceneKey.Lockscreen }
+val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen }
val Kosmos.sceneContainerConfig by Fixture { SceneContainerConfig(sceneKeys, initialSceneKey) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
index c208aad78295..59a01cbedc5c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.shared.model
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
new file mode 100644
index 000000000000..d79374021968
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+
+val Kosmos.keyguardOcclusionInteractor by
+ Kosmos.Fixture {
+ KeyguardOcclusionInteractor(
+ scope = testScope.backgroundScope,
+ repository = keyguardOcclusionRepository,
+ powerInteractor = powerInteractor,
+ transitionInteractor = keyguardTransitionInteractor,
+ keyguardInteractor = keyguardInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt
new file mode 100644
index 000000000000..9e34fe8d7c61
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.domain.interactor
+
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.power.domain.interactor.powerInteractor
+
+val Kosmos.statusBarKeyguardViewManagerInteractor by
+ Kosmos.Fixture {
+ StatusBarKeyguardViewManagerInteractor(
+ keyguardTransitionInteractor = this.keyguardTransitionInteractor,
+ keyguardOcclusionInteractor = this.keyguardOcclusionInteractor,
+ powerInteractor = this.powerInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelKosmos.kt
new file mode 100644
index 000000000000..7ffa262d55a4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackInteractor
+
+val Kosmos.notificationViewFlipperViewModel by Fixture {
+ NotificationViewFlipperViewModel(
+ dumpManager = dumpManager,
+ stackInteractor = notificationStackInteractor,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorKosmos.kt
index c6ae21505c68..db6ba6284599 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorKosmos.kt
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.scene.shared.model
+package com.android.systemui.statusbar.notification.stack.domain.interactor
-data class UserActionResult(
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
- /** The scene we should be transitioning due to the [UserAction]. */
- val toScene: SceneKey,
-
- /**
- * The key of the transition that should be used, if a specific one should be used.
- *
- * If `null`, the transition used will be the corresponding transition from the collection
- * passed into the UI layer.
- */
- val transitionKey: TransitionKey? = null,
-)
+val Kosmos.notificationStackInteractor by Fixture {
+ NotificationStackInteractor(
+ keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
index 25e3eac0b4b7..f1767ebb10d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
@@ -16,16 +16,15 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.footerViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.notificationShelfViewModel
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackInteractor
import com.android.systemui.statusbar.policy.domain.interactor.userSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import java.util.Optional
@@ -37,8 +36,7 @@ val Kosmos.notificationListViewModel by Fixture {
Optional.of(footerViewModel),
Optional.of(notificationListLoggerViewModel),
activeNotificationsInteractor,
- keyguardInteractor,
- powerInteractor,
+ notificationStackInteractor,
remoteInputInteractor,
seenNotificationsInteractor,
shadeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
index d79633ae72ba..bada2a61995d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -24,6 +25,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.notif
val Kosmos.notificationStackAppearanceViewModel by Fixture {
NotificationStackAppearanceViewModel(
+ dumpManager = dumpManager,
stackAppearanceInteractor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
sceneInteractor = sceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 832344d7a822..c01366489c69 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -17,12 +17,15 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.aodToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.dozingToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel
@@ -48,6 +51,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.sharedNotificationContainerViewModel by Fixture {
SharedNotificationContainerViewModel(
interactor = sharedNotificationContainerInteractor,
+ dumpManager = dumpManager,
applicationScope = applicationCoroutineScope,
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
@@ -55,7 +59,9 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
communalInteractor = communalInteractor,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+ aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
+ dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel,
dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
index 41c11ad61c7f..7a86c4f73a3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
@@ -33,6 +33,7 @@ import com.android.systemui.settings.userTracker
import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor
import com.android.systemui.shade.shadeController
import com.android.systemui.shade.shadeViewController
+import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.notification.collection.provider.launchFullScreenIntentProvider
import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider
import com.android.systemui.statusbar.notification.notificationTransitionAnimatorControllerProvider
@@ -59,6 +60,7 @@ val Kosmos.statusBarNotificationActivityStarter by
notificationVisibilityProvider,
headsUpManager,
activityStarter,
+ commandQueue,
notificationClickNotifier,
statusBarKeyguardViewManager,
keyguardManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DevicePostureControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DevicePostureControllerKosmos.kt
new file mode 100644
index 000000000000..89eaf15a52d9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DevicePostureControllerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.devicePostureController by Kosmos.Fixture { mock<DevicePostureController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
new file mode 100644
index 000000000000..df6fc41e5f3e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index be57658a4266..4aa85a79c934 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -19,13 +19,29 @@ import android.testing.LeakCheck;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+import java.util.ArrayList;
+import java.util.List;
+
public class FakeRotationLockController extends BaseLeakChecker<RotationLockControllerCallback>
implements RotationLockController {
+ private boolean mIsLocked = false;
+ private final List<RotationLockControllerCallback> mCallbacks = new ArrayList<>();
public FakeRotationLockController(LeakCheck test) {
super(test, "rotation");
}
@Override
+ public void addCallback(RotationLockControllerCallback listener) {
+ mCallbacks.add(listener);
+ listener.onRotationLockStateChanged(mIsLocked, isRotationLockAffordanceVisible());
+ }
+
+ @Override
+ public void removeCallback(RotationLockControllerCallback listener) {
+ mCallbacks.remove(listener);
+ }
+
+ @Override
public void setListening(boolean listening) {
}
@@ -42,12 +58,15 @@ public class FakeRotationLockController extends BaseLeakChecker<RotationLockCont
@Override
public boolean isRotationLocked() {
- return false;
+ return mIsLocked;
}
@Override
public void setRotationLocked(boolean locked, String caller) {
-
+ mIsLocked = locked;
+ for (RotationLockControllerCallback callback : mCallbacks) {
+ callback.onRotationLockStateChanged(locked, isRotationLockAffordanceVisible());
+ }
}
@Override
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
index 3f20df3376d9..a3b1a0eb260d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
@@ -25,7 +25,6 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.mediaOutputDialogFactory
-import com.android.systemui.plugins.activityStarter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -43,7 +42,7 @@ val Kosmos.localMediaRepositoryFactory: LocalMediaRepositoryFactory by
Kosmos.Fixture { FakeLocalMediaRepositoryFactory { localMediaRepository } }
val Kosmos.mediaOutputActionsInteractor by
- Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) }
+ Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory) }
val Kosmos.mediaControllerRepository by Kosmos.Fixture { FakeMediaControllerRepository() }
val Kosmos.mediaOutputInteractor by
Kosmos.Fixture {
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index cffcd0941df8..71d291a6f877 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -61,6 +61,12 @@ message CriticalEventProto {
NativeCrash native_crash = 6;
SystemServerStarted system_server_started = 7;
InstallPackages install_packages = 8;
+ ExcessiveBinderCalls excessive_binder_calls = 9;
+ }
+
+ message ExcessiveBinderCalls {
+ // The uid sending many calls.
+ optional int32 uid = 1;
}
message InstallPackages {}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 41a4a1ad1ccb..178102e2876d 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -77,6 +77,7 @@ java_library {
libs: [
"android.test.mock",
"framework-minus-apex.ravenwood",
+ "ravenwood-framework",
"services.core.ravenwood",
"junit",
],
@@ -85,6 +86,21 @@ java_library {
jarjar_rules: ":ravenwood-services-jarjar-rules",
}
+// Separated out from ravenwood-junit-impl since it needs to compile
+// against `module_current`
+java_library {
+ name: "ravenwood-junit-impl-flag",
+ srcs: [
+ "junit-flag-src/**/*.java",
+ ],
+ sdk_version: "module_current",
+ libs: [
+ "junit",
+ "flag-junit",
+ ],
+ visibility: ["//visibility:public"],
+}
+
// Carefully compiles against only test_current to support tests that
// want to verify they're unbundled. The "impl" library above is what
// ships inside the Ravenwood environment to actually drive any API
@@ -94,14 +110,31 @@ java_library {
srcs: [
"junit-src/**/*.java",
"junit-stub-src/**/*.java",
+ "junit-flag-src/**/*.java",
],
sdk_version: "test_current",
libs: [
"junit",
+ "flag-junit",
],
visibility: ["//visibility:public"],
}
+// Library used to publish a handful of `android.ravenwood` APIs into
+// the Ravenwood BCP; we don't want to publish these APIs into the BCP
+// on physical devices, which is why this is a separate library
+java_library {
+ name: "ravenwood-framework",
+ srcs: [
+ "framework-src/**/*.java",
+ ],
+ libs: [
+ "framework-minus-apex.ravenwood",
+ ],
+ sdk_version: "core_current",
+ visibility: ["//visibility:public"],
+}
+
java_host_for_device {
name: "androidx.test.monitor-for-device",
libs: [
diff --git a/ravenwood/framework-src/android/ravenwood/example/BlueManager.java b/ravenwood/framework-src/android/ravenwood/example/BlueManager.java
new file mode 100644
index 000000000000..fc713b1bd164
--- /dev/null
+++ b/ravenwood/framework-src/android/ravenwood/example/BlueManager.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.example;
+
+import android.annotation.SystemService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+@SystemService(BlueManager.SERVICE_NAME)
+public class BlueManager {
+ public static final String SERVICE_NAME = "example_blue";
+
+ public String getInterfaceDescriptor() {
+ try {
+ return ServiceManager.getService(SERVICE_NAME).getInterfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/ravenwood/framework-src/android/ravenwood/example/RedManager.java b/ravenwood/framework-src/android/ravenwood/example/RedManager.java
new file mode 100644
index 000000000000..381a901f234a
--- /dev/null
+++ b/ravenwood/framework-src/android/ravenwood/example/RedManager.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.example;
+
+import android.annotation.SystemService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+@SystemService(RedManager.SERVICE_NAME)
+public class RedManager {
+ public static final String SERVICE_NAME = "example_red";
+
+ public String getInterfaceDescriptor() {
+ try {
+ return ServiceManager.getService(SERVICE_NAME).getInterfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java b/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
new file mode 100644
index 000000000000..9d6277473298
--- /dev/null
+++ b/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.platform.test.flag.junit;
+
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.IFlagsValueProvider;
+
+/**
+ * Offer to create {@link CheckFlagsRule} instances that are useful on the Ravenwood deviceless
+ * testing environment.
+ *
+ * At the moment, default flag values are not available on Ravenwood, so the only options offered
+ * here are "all-on" and "all-off" options. Tests that want to exercise specific flag states should
+ * use {@link android.platform.test.flag.junit.SetFlagsRule}.
+ */
+public class RavenwoodFlagsValueProvider {
+ /**
+ * Create a {@link CheckFlagsRule} instance where flags are in an "all-on" state.
+ */
+ public static CheckFlagsRule createAllOnCheckFlagsRule() {
+ return new CheckFlagsRule(new IFlagsValueProvider() {
+ @Override
+ public boolean getBoolean(String flag) {
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Create a {@link CheckFlagsRule} instance where flags are in an "all-off" state.
+ */
+ public static CheckFlagsRule createAllOffCheckFlagsRule() {
+ return new CheckFlagsRule(new IFlagsValueProvider() {
+ @Override
+ public boolean getBoolean(String flag) {
+ return false;
+ }
+ });
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index c17d0903f856..109ef76b535f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -26,6 +26,8 @@ import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.ravenwood.example.BlueManager;
+import android.ravenwood.example.RedManager;
import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.util.Singleton;
@@ -53,16 +55,23 @@ public class RavenwoodContext extends MockContext {
mPackageName = packageName;
mMainThread = mainThread;
+ // Services provided by a typical shipping device
registerService(ClipboardManager.class,
- Context.CLIPBOARD_SERVICE, asSingleton(() ->
+ Context.CLIPBOARD_SERVICE, memoize(() ->
new ClipboardManager(this, getMainThreadHandler())));
registerService(PermissionEnforcer.class,
Context.PERMISSION_ENFORCER_SERVICE, () -> mEnforcer);
registerService(SerialManager.class,
- Context.SERIAL_SERVICE, asSingleton(() ->
+ Context.SERIAL_SERVICE, memoize(() ->
new SerialManager(this, ISerialManager.Stub.asInterface(
ServiceManager.getService(Context.SERIAL_SERVICE)))
));
+
+ // Additional services we provide for testing purposes
+ registerService(BlueManager.class,
+ BlueManager.SERVICE_NAME, memoize(() -> new BlueManager()));
+ registerService(RedManager.class,
+ RedManager.SERVICE_NAME, memoize(() -> new RedManager()));
}
@Override
@@ -143,9 +152,12 @@ public class RavenwoodContext extends MockContext {
}
/**
- * Wrap the given {@link Supplier} to become a memoized singleton.
+ * Wrap the given {@link Supplier} to become memoized.
+ *
+ * The underlying {@link Supplier} will only be invoked once, and that result will be cached
+ * and returned for any future requests.
*/
- private static <T> Supplier<T> asSingleton(ThrowingSupplier<T> supplier) {
+ private static <T> Supplier<T> memoize(ThrowingSupplier<T> supplier) {
final Singleton<T> singleton = new Singleton<>() {
@Override
protected T create() {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index 3de96c0990ea..cd6b61df392f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -19,13 +19,19 @@ package android.platform.test.ravenwood;
import android.content.ClipboardManager;
import android.hardware.SerialManager;
import android.os.SystemClock;
+import android.ravenwood.example.BlueManager;
+import android.ravenwood.example.RedManager;
import android.util.ArrayMap;
+import android.util.ArraySet;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.utils.TimingsTraceAndSlog;
+import java.util.List;
+import java.util.Set;
+
public class RavenwoodSystemServer {
/**
* Set of services that we know how to provide under Ravenwood. We keep this set distinct
@@ -37,16 +43,21 @@ public class RavenwoodSystemServer {
*/
private static final ArrayMap<Class<?>, String> sKnownServices = new ArrayMap<>();
- // TODO: expand SystemService API to support dependency expression, so we don't need test
- // authors to exhaustively declare all transitive services
-
static {
+ // Services provided by a typical shipping device
sKnownServices.put(ClipboardManager.class,
"com.android.server.FakeClipboardService$Lifecycle");
sKnownServices.put(SerialManager.class,
"com.android.server.SerialService$Lifecycle");
+
+ // Additional services we provide for testing purposes
+ sKnownServices.put(BlueManager.class,
+ "com.android.server.example.BlueManagerService$Lifecycle");
+ sKnownServices.put(RedManager.class,
+ "com.android.server.example.RedManagerService$Lifecycle");
}
+ private static Set<Class<?>> sStartedServices;
private static TimingsTraceAndSlog sTimings;
private static SystemServiceManager sServiceManager;
@@ -54,6 +65,7 @@ public class RavenwoodSystemServer {
// Avoid overhead if no services required
if (rule.mServicesRequired.isEmpty()) return;
+ sStartedServices = new ArraySet<>();
sTimings = new TimingsTraceAndSlog();
sServiceManager = new SystemServiceManager(rule.mContext);
sServiceManager.setStartInfo(false,
@@ -61,17 +73,7 @@ public class RavenwoodSystemServer {
SystemClock.uptimeMillis());
LocalServices.addService(SystemServiceManager.class, sServiceManager);
- for (Class<?> service : rule.mServicesRequired) {
- final String target = sKnownServices.get(service);
- if (target == null) {
- throw new RuntimeException("The requested service " + service
- + " is not yet supported under the Ravenwood deviceless testing "
- + "environment; consider requesting support from the API owner or "
- + "consider using Mockito; more details at go/ravenwood-docs");
- } else {
- sServiceManager.startService(target);
- }
- }
+ startServices(rule.mServicesRequired);
sServiceManager.sealStartedServices();
// TODO: expand to include additional boot phases when relevant
@@ -85,5 +87,26 @@ public class RavenwoodSystemServer {
LocalServices.removeServiceForTest(SystemServiceManager.class);
sServiceManager = null;
sTimings = null;
+ sStartedServices = null;
+ }
+
+ private static void startServices(List<Class<?>> serviceClasses) {
+ for (Class<?> serviceClass : serviceClasses) {
+ // Quietly ignore duplicate requests if service already started
+ if (sStartedServices.contains(serviceClass)) continue;
+ sStartedServices.add(serviceClass);
+
+ final String serviceName = sKnownServices.get(serviceClass);
+ if (serviceName == null) {
+ throw new RuntimeException("The requested service " + serviceClass
+ + " is not yet supported under the Ravenwood deviceless testing "
+ + "environment; consider requesting support from the API owner or "
+ + "consider using Mockito; more details at go/ravenwood-docs");
+ }
+
+ // Start service and then depth-first traversal of any dependencies
+ final SystemService instance = sServiceManager.startService(serviceName);
+ startServices(instance.getDependencies());
+ }
}
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index a520d4ccafa1..52ea3402fa62 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -35,6 +35,8 @@ import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
@@ -127,7 +129,7 @@ public class RavenwoodRule implements TestRule {
final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
- final ArraySet<Class<?>> mServicesRequired = new ArraySet<>();
+ final List<Class<?>> mServicesRequired = new ArrayList<>();
volatile Context mContext;
volatile Instrumentation mInstrumentation;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index cc94090d9ec7..9057d163957e 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -42,7 +42,6 @@ public class ClassLoadHook {
public static final String KEYBOARD_PATHS = "keyboard_paths";
public static final String GRAPHICS_NATIVE_CLASSES = "graphics_native_classes";
- public static final String VALUE_N_A = "**n/a**";
public static final String LIBANDROID_RUNTIME_NAME = "android_runtime";
private static String sInitialDir = new File("").getAbsolutePath();
@@ -130,8 +129,6 @@ public class ClassLoadHook {
}
setProperty(CORE_NATIVE_CLASSES, jniClasses);
setProperty(GRAPHICS_NATIVE_CLASSES, "");
- setProperty(ICU_DATA_PATH, VALUE_N_A);
- setProperty(KEYBOARD_PATHS, VALUE_N_A);
RavenwoodUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME);
}
diff --git a/ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesDependenciesTest.java b/ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesDependenciesTest.java
new file mode 100644
index 000000000000..efe468d0df25
--- /dev/null
+++ b/ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesDependenciesTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ravenwood;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.ravenwood.example.BlueManager;
+import android.ravenwood.example.RedManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodServicesDependenciesTest {
+ // NOTE: we carefully only ask for RedManager here, and rely on Ravenwood internals to spin
+ // up the implicit dependency on BlueManager
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProcessSystem()
+ .setServicesRequired(RedManager.class)
+ .build();
+
+ @Test
+ public void testDirect() {
+ final RedManager red = mRavenwood.getContext().getSystemService(
+ RedManager.class);
+ assertEquals("blue+red", red.getInterfaceDescriptor());
+ }
+
+ @Test
+ public void testIndirect() {
+ final BlueManager blue = mRavenwood.getContext().getSystemService(
+ BlueManager.class);
+ assertEquals("blue", blue.getInterfaceDescriptor());
+ }
+}
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 9179a621d0df..7c0cee812996 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -112,6 +112,24 @@ public class MyCodeTest {
This naturally composes together well with any `RavenwoodRule` that your test may need.
+While `SetFlagsRule` is generally a best-practice (as it can explicitly confirm behaviors for both "on" and "off" states), you may need to write tests that use `CheckFlagsRule` (such as when writing CTS). Ravenwood currently supports `CheckFlagsRule` by offering "all-on" and "all-off" behaviors:
+
+```
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+@RunWith(AndroidJUnit4.class)
+public class MyCodeTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
+```
+
+Ravenwood currently doesn't have knowledge of the "default" value of any flags, so using `createAllOnCheckFlagsRule()` is recommended to verify the widest possible set of behaviors. The example code above falls back to using default values from `DeviceFlagsValueProvider` when not running on Ravenwood.
+
## Strategies for migration/bivalent tests
Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can be dual-compiled to run on both a real Android device and under a Ravenwood environment.
diff --git a/sax/tests/saxtests/Android.bp b/sax/tests/saxtests/Android.bp
index cbd19c36dd73..446ee9394179 100644
--- a/sax/tests/saxtests/Android.bp
+++ b/sax/tests/saxtests/Android.bp
@@ -15,6 +15,9 @@ android_test {
"android.test.runner",
"android.test.base",
],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "androidx.test.rules",
+ ],
platform_apis: true,
}
diff --git a/sax/tests/saxtests/src/android/sax/SafeSaxTest.java b/sax/tests/saxtests/src/android/sax/SafeSaxTest.java
index a68fc9ab2290..2a08f5480825 100644
--- a/sax/tests/saxtests/src/android/sax/SafeSaxTest.java
+++ b/sax/tests/saxtests/src/android/sax/SafeSaxTest.java
@@ -17,18 +17,16 @@
package android.sax;
import android.graphics.Bitmap;
-import android.sax.Element;
-import android.sax.ElementListener;
-import android.sax.EndTextElementListener;
-import android.sax.RootElement;
-import android.sax.StartElementListener;
-import android.sax.TextElementListener;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.Xml;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.frameworks.saxtests.R;
import com.android.internal.util.XmlUtils;
+
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
@@ -40,8 +38,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
-import com.android.frameworks.saxtests.R;
-
public class SafeSaxTest extends AndroidTestCase {
private static final String TAG = SafeSaxTest.class.getName();
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index a754ba547767..997f3af3533e 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -59,6 +59,13 @@ flag {
}
flag {
+ name: "enable_magnification_one_finger_panning_gesture"
+ namespace: "accessibility"
+ description: "Whether to allow easy-mode (one finger panning gesture) for magnification"
+ bug: "282039824"
+}
+
+flag {
name: "fix_drag_pointer_when_ending_drag"
namespace: "accessibility"
description: "Send the correct pointer id when transitioning from dragging to delegating states."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 43c018cfeea3..cbb66dc18f28 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4659,8 +4659,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
- new AccessibilityShellCommand(this, mSystemActionPerformer).exec(this, in, out, err, args,
- callback, resultReceiver);
+ new AccessibilityShellCommand(mContext, this, mSystemActionPerformer)
+ .exec(this, in, out, err, args, callback, resultReceiver);
}
private final class InteractionBridge {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index 8cf5547b05ec..490803228337 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -16,8 +16,10 @@
package com.android.server.accessibility;
+import android.Manifest;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.content.Context;
import android.os.Binder;
import android.os.Process;
import android.os.ShellCommand;
@@ -26,18 +28,27 @@ import android.os.UserHandle;
import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerInternal;
+import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
/**
* Shell command implementation for the accessibility manager service
*/
final class AccessibilityShellCommand extends ShellCommand {
- final @NonNull AccessibilityManagerService mService;
- final @NonNull SystemActionPerformer mSystemActionPerformer;
- final @NonNull WindowManagerInternal mWindowManagerService;
+ @NonNull
+ final Context mContext;
+ @NonNull
+ final AccessibilityManagerService mService;
+ @NonNull
+ final SystemActionPerformer mSystemActionPerformer;
+ @NonNull
+ final WindowManagerInternal mWindowManagerService;
- AccessibilityShellCommand(@NonNull AccessibilityManagerService service,
+ AccessibilityShellCommand(@NonNull Context context,
+ @NonNull AccessibilityManagerService service,
@NonNull SystemActionPerformer systemActionPerformer) {
+ mContext = context;
mService = service;
mSystemActionPerformer = systemActionPerformer;
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
@@ -61,6 +72,8 @@ final class AccessibilityShellCommand extends ShellCommand {
case "start-trace":
case "stop-trace":
return mService.getTraceManager().onShellCommand(cmd, this);
+ case "check-hidraw":
+ return checkHidraw();
}
return -1;
}
@@ -106,6 +119,67 @@ final class AccessibilityShellCommand extends ShellCommand {
return -1;
}
+ private int checkHidraw() {
+ mContext.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+ "Missing MANAGE_ACCESSIBILITY permission");
+ String subcommand = getNextArgRequired();
+ File hidrawNode = new File(getNextArgRequired());
+ switch (subcommand) {
+ case "read" -> {
+ return checkHidrawRead(hidrawNode);
+ }
+ case "write" -> {
+ return checkHidrawWrite(hidrawNode);
+ }
+ case "descriptor" -> {
+ return checkHidrawDescriptor(hidrawNode);
+ }
+ default -> {
+ getErrPrintWriter().print("Unknown subcommand " + subcommand);
+ return -1;
+ }
+ }
+ }
+
+ private int checkHidrawRead(File hidrawNode) {
+ if (!hidrawNode.canRead()) {
+ getErrPrintWriter().println("Unable to read from " + hidrawNode);
+ return -1;
+ }
+ // Tests executing this command using UiAutomation#executeShellCommand will not receive
+ // the command's exit value, so print the path to stdout to indicate success.
+ getOutPrintWriter().print(hidrawNode.getAbsolutePath());
+ return 0;
+ }
+
+ private int checkHidrawWrite(File hidrawNode) {
+ if (!hidrawNode.canWrite()) {
+ getErrPrintWriter().println("Unable to write to " + hidrawNode);
+ return -1;
+ }
+ // Tests executing this command using UiAutomation#executeShellCommand will not receive
+ // the command's exit value, so print the path to stdout to indicate success.
+ getOutPrintWriter().print(hidrawNode.getAbsolutePath());
+ return 0;
+ }
+
+ private int checkHidrawDescriptor(File hidrawNode) {
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ BrailleDisplayConnection.createScannerForShell();
+ byte[] descriptor = scanner.getDeviceReportDescriptor(hidrawNode.toPath());
+ if (descriptor == null) {
+ getErrPrintWriter().println("Unable to read descriptor for " + hidrawNode);
+ return -1;
+ }
+ try {
+ // Print the descriptor bytes to stdout.
+ getRawOutputStream().write(descriptor);
+ return 0;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private Integer parseUserId() {
final String option = getNextOption();
if (option != null) {
@@ -131,6 +205,8 @@ final class AccessibilityShellCommand extends ShellCommand {
pw.println(" Get whether binding to services provided by instant apps is allowed.");
pw.println(" call-system-action <ACTION_ID>");
pw.println(" Calls the system action with the given action id.");
+ pw.println(" check-hidraw [read|write|descriptor] <HIDRAW_NODE_PATH>");
+ pw.println(" Checks if the system can perform various actions on the HIDRAW node.");
mService.getTraceManager().onHelp(pw);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
index 40b6ff01965e..8b41873636a9 100644
--- a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
@@ -116,6 +116,13 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
}
/**
+ * Used for `cmd accessibility` to check hidraw access.
+ */
+ static BrailleDisplayScanner createScannerForShell() {
+ return getDefaultNativeScanner(new DefaultNativeInterface());
+ }
+
+ /**
* Interface to scan for properties of connected Braille displays.
*
* <p>Helps simplify testing Braille Display APIs using test data without requiring
@@ -125,7 +132,6 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
* @see #getDefaultNativeScanner
* @see #setTestData
*/
- @VisibleForTesting
interface BrailleDisplayScanner {
Collection<Path> getHidrawNodePaths(@NonNull Path directory);
@@ -441,7 +447,7 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
* from HIDRAW nodes and perform ioctls using the provided {@link NativeInterface}.
*/
@VisibleForTesting
- BrailleDisplayScanner getDefaultNativeScanner(@NonNull NativeInterface nativeInterface) {
+ static BrailleDisplayScanner getDefaultNativeScanner(@NonNull NativeInterface nativeInterface) {
Objects.requireNonNull(nativeInterface);
return new BrailleDisplayScanner() {
private static final String HIDRAW_DEVICE_GLOB = "hidraw*";
@@ -576,7 +582,7 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
}
/** Native interface that actually calls native HIDRAW ioctls. */
- private class DefaultNativeInterface implements NativeInterface {
+ private static class DefaultNativeInterface implements NativeInterface {
@Override
public int getHidrawDescSize(int fd) {
return nativeGetHidrawDescSize(fd);
@@ -598,11 +604,11 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
}
}
- private native int nativeGetHidrawDescSize(int fd);
+ private static native int nativeGetHidrawDescSize(int fd);
- private native byte[] nativeGetHidrawDesc(int fd, int descSize);
+ private static native byte[] nativeGetHidrawDesc(int fd, int descSize);
- private native String nativeGetHidrawUniq(int fd);
+ private static native String nativeGetHidrawUniq(int fd);
- private native int nativeGetHidrawBusType(int fd);
+ private static native int nativeGetHidrawBusType(int fd);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f4ea754c21ba..6d1ab9f89f78 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -167,7 +167,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
})
public @interface OverscrollState {}
- @VisibleForTesting boolean mIsSinglePanningEnabled;
+ @VisibleForTesting final OneFingerPanningSettingsProvider mOneFingerPanningSettingsProvider;
private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper;
@@ -201,7 +201,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
displayId,
fullScreenMagnificationVibrationHelper,
/* magnificationLogger= */ null,
- ViewConfiguration.get(context));
+ ViewConfiguration.get(context),
+ new OneFingerPanningSettingsProvider(
+ context,
+ Flags.enableMagnificationOneFingerPanningGesture()
+ ));
}
/** Constructor for tests. */
@@ -218,7 +222,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
int displayId,
FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper,
MagnificationLogger magnificationLogger,
- ViewConfiguration viewConfiguration) {
+ ViewConfiguration viewConfiguration,
+ OneFingerPanningSettingsProvider oneFingerPanningSettingsProvider
+ ) {
super(displayId, detectSingleFingerTripleTap, detectTwoFingerTripleTap,
detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
@@ -301,9 +307,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mPanningScalingState = new PanningScalingState(context);
mSinglePanningState = new SinglePanningState(context);
mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
- setSinglePanningEnabled(
- context.getResources()
- .getBoolean(R.bool.config_enable_a11y_magnification_single_panning));
+ mOneFingerPanningSettingsProvider = oneFingerPanningSettingsProvider;
mOverscrollHandler = new OverscrollHandler();
mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
@@ -317,11 +321,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
transitionTo(mDetectingState);
}
- @VisibleForTesting
- void setSinglePanningEnabled(boolean isEnabled) {
- mIsSinglePanningEnabled = isEnabled;
- }
-
@Override
void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (event.getActionMasked() == ACTION_DOWN) {
@@ -361,6 +360,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
Slog.i(mLogTag, "onDestroy(); delayed = "
+ MotionEventInfo.toString(mDetectingState.mDelayedEventQueue));
}
+ mOneFingerPanningSettingsProvider.unregister();
if (mScreenStateReceiver != null) {
mScreenStateReceiver.unregister();
@@ -524,7 +524,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
&& event.getPointerCount() == 2 // includes the pointer currently being released
&& mPreviousState == mViewportDraggingState) {
// if feature flag is enabled, currently only true on watches
- if (mIsSinglePanningEnabled) {
+ if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
mOverscrollHandler.clearEdgeState();
}
@@ -532,7 +532,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
} else if (action == ACTION_UP || action == ACTION_CANCEL) {
onPanningFinished(event);
// if feature flag is enabled, currently only true on watches
- if (mIsSinglePanningEnabled) {
+ if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
mOverscrollHandler.clearEdgeState();
}
@@ -611,7 +611,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
onPan(second);
mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
- if (mIsSinglePanningEnabled) {
+ if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
mOverscrollHandler.onScrollStateChanged(first, second);
}
return /* event consumed: */ true;
@@ -1000,21 +1000,23 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
&& event.getPointerCount() == 2) {
transitionToViewportDraggingStateAndClear(event);
} else if (isActivated() && event.getPointerCount() == 2) {
- if (mIsSinglePanningEnabled
+ if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
&& overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
- } // TODO(b/319537921): should there be an else here?
- //Primary pointer is swiping, so transit to PanningScalingState
- transitToPanningScalingStateAndClear();
- } else if (mIsSinglePanningEnabled
+ } else {
+ //Primary pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
+ }
+ } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
&& isActivated()
&& event.getPointerCount() == 1) {
if (overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
- } // TODO(b/319537921): should there be an else here?
- transitToSinglePanningStateAndClear();
+ } else {
+ transitToSinglePanningStateAndClear();
+ }
} else if (!mIsTwoFingerCountReached) {
// If it is a two-finger gesture, do not transition to the
// delegating state to ensure the reachability of
@@ -1253,21 +1255,23 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
transitionToViewportDraggingStateAndClear(event);
} else if (isActivated() && event.getPointerCount() == 2) {
- if (mIsSinglePanningEnabled
+ if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
&& overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
+ } else {
+ //Primary pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
}
- //Primary pointer is swiping, so transit to PanningScalingState
- transitToPanningScalingStateAndClear();
- } else if (mIsSinglePanningEnabled
+ } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
&& isActivated()
&& event.getPointerCount() == 1) {
if (overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
+ } else {
+ transitToSinglePanningStateAndClear();
}
- transitToSinglePanningStateAndClear();
} else {
transitionToDelegatingStateAndClear();
}
@@ -1629,7 +1633,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
+ ", mPreviousState=" + State.nameOf(mPreviousState)
+ ", mMagnificationController=" + mFullScreenMagnificationController
+ ", mDisplayId=" + mDisplayId
- + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled
+ + ", mIsSinglePanningEnabled="
+ + mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
+ ", mOverscrollHandler=" + mOverscrollHandler
+ '}';
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/OneFingerPanningSettingsProvider.java b/services/accessibility/java/com/android/server/accessibility/magnification/OneFingerPanningSettingsProvider.java
new file mode 100644
index 000000000000..3cdaf98780d9
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/OneFingerPanningSettingsProvider.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accessibility.magnification;
+
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Provider for secure settings {@link Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED}.
+ */
+public class OneFingerPanningSettingsProvider {
+
+ @VisibleForTesting
+ static final String KEY = Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED;
+ private static final Uri URI = Settings.Secure.getUriFor(KEY);
+ private AtomicBoolean mCached = new AtomicBoolean();
+ @VisibleForTesting
+ ContentObserver mObserver;
+ @VisibleForTesting
+ ContentResolver mContentResolver;
+
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {
+ int OFF = 0;
+ int ON = 1;
+ }
+
+ public OneFingerPanningSettingsProvider(
+ Context context,
+ boolean featureFlagEnabled
+ ) {
+ var defaultValue = isOneFingerPanningEnabledDefault(context);
+ if (featureFlagEnabled) {
+ mContentResolver = context.getContentResolver();
+ mObserver = new ContentObserver(context.getMainThreadHandler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mCached.set(isOneFingerPanningEnabledInSetting(context, defaultValue));
+ }
+ };
+ mCached.set(isOneFingerPanningEnabledInSetting(context, defaultValue));
+ mContentResolver.registerContentObserver(URI, false, mObserver);
+ } else {
+ mCached.set(defaultValue);
+ }
+ }
+
+ /** Returns whether one finger panning is enabled.. */
+ public boolean isOneFingerPanningEnabled() {
+ return mCached.get();
+ }
+
+ /** Unregister content observer for listening to secure settings. */
+ public void unregister() {
+ if (mContentResolver != null) {
+ mContentResolver.unregisterContentObserver(mObserver);
+ }
+ mContentResolver = null;
+ }
+
+ private boolean isOneFingerPanningEnabledInSetting(Context context, boolean defaultValue) {
+ return State.ON == Settings.Secure.getIntForUser(
+ mContentResolver,
+ KEY,
+ (defaultValue ? State.ON : State.OFF),
+ context.getUserId());
+ }
+
+ @VisibleForTesting
+ static boolean isOneFingerPanningEnabledDefault(Context context) {
+ boolean oneFingerPanningDefaultValue;
+ try {
+ oneFingerPanningDefaultValue = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enable_a11y_magnification_single_panning);
+ } catch (Resources.NotFoundException e) {
+ oneFingerPanningDefaultValue = false;
+ }
+ return oneFingerPanningDefaultValue;
+ }
+}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6f45f60f6dfa..29b9d441cf38 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -476,8 +476,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
} else {
// If the package is being updated, we'll receive a PACKAGE_ADDED
// shortly, otherwise it is removed permanently.
- final boolean packageRemovedPermanently = (extras == null
- || !extras.getBoolean(Intent.EXTRA_REPLACING, false));
+ boolean isReplacing = extras != null && extras.getBoolean(Intent.EXTRA_REPLACING,
+ false);
+ boolean isArchival = extras != null && extras.getBoolean(Intent.EXTRA_ARCHIVAL,
+ false);
+ final boolean packageRemovedPermanently =
+ (extras == null || !isReplacing || (isReplacing && isArchival));
if (packageRemovedPermanently) {
for (String pkgName : pkgList) {
@@ -2074,6 +2078,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
int appWidgetId, int viewId, long requestId) {
try {
+ Slog.d(TAG, "Trying to notify widget view data changed");
callbacks.viewDataChanged(appWidgetId, viewId);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2158,6 +2163,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
int appWidgetId, RemoteViews views, long requestId) {
try {
+ Slog.d(TAG, "Trying to notify widget update for package "
+ + (views == null ? "null" : views.getPackage())
+ + " with widget id: " + appWidgetId);
callbacks.updateAppWidget(appWidgetId, views);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2196,6 +2204,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
int appWidgetId, AppWidgetProviderInfo info, long requestId) {
try {
+ Slog.d(TAG, "Trying to notify provider update");
callbacks.providerChanged(appWidgetId, info);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2239,6 +2248,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private void handleNotifyAppWidgetRemoved(Host host, IAppWidgetHost callbacks, int appWidgetId,
long requestId) {
try {
+ Slog.d(TAG, "Trying to notify widget removed");
callbacks.appWidgetRemoved(appWidgetId);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2286,6 +2296,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) {
try {
+ Slog.d(TAG, "Trying to notify widget providers changed");
callbacks.providersChanged();
} catch (RemoteException re) {
synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index e1291e5f75ec..285e54c30167 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -33,8 +33,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
@@ -251,6 +253,26 @@ final class AutofillManagerServiceImpl
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
+ final List<ResolveInfo> resolveInfos =
+ getContext().getPackageManager().queryIntentServicesAsUser(
+ new Intent(AutofillService.SERVICE_INTERFACE),
+ PackageManager.GET_META_DATA,
+ mUserId);
+ boolean currentPackageStillHasAutofillIntentFilter = false;
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo.getComponentName().equals(serviceComponent)) {
+ currentPackageStillHasAutofillIntentFilter = true;
+ break;
+ }
+ }
+ if (!currentPackageStillHasAutofillIntentFilter) {
+ Slog.w(TAG,
+ "Autofill service from '" + serviceComponent.getPackageName() + "' does"
+ + "not have intent filter " + AutofillService.SERVICE_INTERFACE);
+ throw new SecurityException("Service does not declare intent filter "
+ + AutofillService.SERVICE_INTERFACE);
+ }
mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId);
return mInfo.getServiceInfo();
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d779fbf2eabc..551297b253d1 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -4782,7 +4782,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (isCredmanIntegrationActive(response)) {
- Slog.d(TAG, "Attempting to add Credential Manager callback to pinned entries");
addCredentialManagerCallback(response);
}
@@ -5713,7 +5712,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/* isPrimary= */ true);
updateFillDialogTriggerIdsLocked();
updateTrackedIdsLocked();
-
if (mCurrentViewId == null) {
return;
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e666442af9c9..8fece8257b89 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -167,6 +167,9 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// List of packages that have RestoreAnyVersion set to true but do not support V-> U downgrade.
private List<String> mVToUDenylist;
+ // Whether we have already initialised the V to U allowlist/denylist
+ private Boolean mAreVToUListsSet = false;
+
// Key/value: bookkeeping about staged data and files for agent access
private File mBackupDataName;
private File mStageName;
@@ -235,18 +238,6 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mBackupEligibilityRules = backupEligibilityRules;
- mVToUAllowlist =
- createVToUList(
- Settings.Secure.getStringForUser(
- backupManagerService.getContext().getContentResolver(),
- Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
- mUserId));
- mVToUDenylist =
- createVToUList(
- Settings.Secure.getStringForUser(
- backupManagerService.getContext().getContentResolver(),
- Settings.Secure.V_TO_U_RESTORE_DENYLIST,
- mUserId));
if (targetPackage != null) {
// Single package restore
@@ -661,11 +652,38 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// installed. If the app has not declared that it is prepared to
// handle this case, we do not attempt the restore.
if (mIsSystemRestore
- && isVToUDowngrade(mPmAgent.getSourceSdk(), android.os.Build.VERSION.SDK_INT)) {
+ && isVToUDowngrade(mPmAgent.getSourceSdk(),
+ android.os.Build.VERSION.SDK_INT)) {
+ if (!mAreVToUListsSet) {
+ mVToUAllowlist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ mUserId));
+ mVToUDenylist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_DENYLIST,
+ mUserId));
+ logVToUListsToBMM();
+ mAreVToUListsSet = true;
+ }
if (isPackageEligibleForVToURestore(mCurrentPackage)) {
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ addRestoreOperationTypeToEvent(/* extras= */null));
Slog.i(TAG, "Package " + pkgName
+ " is eligible for V to U downgrade scenario");
} else {
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_NOT_ELIGIBLE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ addRestoreOperationTypeToEvent(/* extras= */null));
String message = "Package not eligible for V to U downgrade scenario";
Slog.i(TAG, pkgName + " : " + message);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
@@ -1696,14 +1714,25 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// (and not in the denylist)
// - The package has restoreAnyVersion set to true and is not part of the denylist
if (mVToUDenylist.contains(mCurrentPackage.packageName)){
+ if (DEBUG) {
+ Slog.i(TAG, mCurrentPackage.packageName + " : Package is in V to U denylist");
+ }
return false;
} else if ((mCurrentPackage.applicationInfo.flags
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
== 0) {
// package has restoreAnyVersion set to false
+ if (DEBUG) {
+ Slog.i(TAG, mCurrentPackage.packageName
+ + " : Package has restoreAnyVersion=false and is in V to U allowlist");
+ }
return mVToUAllowlist.contains(mCurrentPackage.packageName);
} else {
// package has restoreAnyVersion set to true and is nor in denylist
+ if (DEBUG) {
+ Slog.i(TAG, mCurrentPackage.packageName
+ + " : Package has restoreAnyVersion=true and is not in V to U denylist");
+ }
return true;
}
}
@@ -1748,4 +1777,31 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
monitoringExtras);
}
+ private void logVToUListsToBMM() {
+ // send a BMM event with the allowlist
+ Bundle monitoringExtrasAllowlist =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null,
+ BackupManagerMonitor.EXTRA_LOG_V_TO_U_ALLOWLIST,
+ mVToUAllowlist.toString());
+ monitoringExtrasAllowlist = addRestoreOperationTypeToEvent(monitoringExtrasAllowlist);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_SET_LIST,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtrasAllowlist);
+ // send a BMM event with the denylist
+ Bundle monitoringExtrasDenylist =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null,
+ BackupManagerMonitor.EXTRA_LOG_V_TO_U_DENYLIST,
+ mVToUDenylist.toString());
+ monitoringExtrasDenylist = addRestoreOperationTypeToEvent(monitoringExtrasDenylist);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_SET_LIST,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtrasDenylist);
+ }
+
}
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
index 797aed9297a3..6d315ba38b31 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
@@ -146,7 +146,6 @@ public class BackupManagerMonitorDumpsysUtils {
+ eventBundle.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME));
}
- // TODO(b/296818666): add extras to the events
addAgentLogsIfAvailable(eventBundle, pw);
addExtrasIfAvailable(eventBundle, pw);
} catch (java.io.IOException e) {
@@ -203,6 +202,11 @@ public class BackupManagerMonitorDumpsysUtils {
* EXTRA_LOG_RESTORE_VERSION [int]: the version of the package on the source
* EXTRA_LOG_RESTORE_ANYWAY [bool]: if the package allows restore any version
* EXTRA_LOG_RESTORE_VERSION_TARGET [int]: an extra to record the package version on the target
+ *
+ * When we are performing a V to U downgrade (event with id V_TO_U_RESTORE_SET_LIST) we record
+ * the value of the V to U allowlist and denylist:
+ * EXTRA_LOG_V_TO_U_ALLOWLIST[string]
+ * EXTRA_LOG_V_TO_U_DENYLIST[string]
*/
private void addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw) {
if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID) ==
@@ -216,12 +220,29 @@ public class BackupManagerMonitorDumpsysUtils {
+ eventBundle.getLong(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION));
}
if (eventBundle.containsKey(
- BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) {
+ BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) {
pw.println("\t\tPackage version on target: "
+ eventBundle.getLong(
BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION));
}
}
+
+ if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID)
+ == BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_SET_LIST) {
+ if (eventBundle.containsKey(
+ BackupManagerMonitor.EXTRA_LOG_V_TO_U_DENYLIST)) {
+ pw.println("\t\tV to U Denylist : "
+ + eventBundle.getString(
+ BackupManagerMonitor.EXTRA_LOG_V_TO_U_DENYLIST));
+ }
+
+ if (eventBundle.containsKey(
+ BackupManagerMonitor.EXTRA_LOG_V_TO_U_ALLOWLIST)) {
+ pw.println("\t\tV to U Allowllist : "
+ + eventBundle.getString(
+ BackupManagerMonitor.EXTRA_LOG_V_TO_U_ALLOWLIST));
+ }
+ }
}
/*
@@ -342,10 +363,14 @@ public class BackupManagerMonitorDumpsysUtils {
case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE ->
"Transport error full restore";
case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE -> "Restore complete";
- case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE ->
- "Start package restore";
- case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE ->
- "Agent failure";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE -> "Start package restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE -> "Agent failure";
+ case BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE ->
+ "V to U restore pkg eligible";
+ case BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_NOT_ELIGIBLE ->
+ "V to U restore pkg not eligible";
+ case BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_SET_LIST ->
+ "V to U restore lists";
default -> "Unknown log event ID: " + code;
};
return id;
diff --git a/services/companion/java/com/android/server/companion/AssociationStore.java b/services/companion/java/com/android/server/companion/AssociationStore.java
deleted file mode 100644
index 01905bb2297f..000000000000
--- a/services/companion/java/com/android/server/companion/AssociationStore.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.companion.AssociationInfo;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * Interface for a store of {@link AssociationInfo}-s.
- */
-public interface AssociationStore {
-
- @IntDef(prefix = { "CHANGE_TYPE_" }, value = {
- CHANGE_TYPE_ADDED,
- CHANGE_TYPE_REMOVED,
- CHANGE_TYPE_UPDATED_ADDRESS_CHANGED,
- CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface ChangeType {}
-
- int CHANGE_TYPE_ADDED = 0;
- int CHANGE_TYPE_REMOVED = 1;
- int CHANGE_TYPE_UPDATED_ADDRESS_CHANGED = 2;
- int CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED = 3;
-
- /** Listener for any changes to {@link AssociationInfo}-s. */
- interface OnChangeListener {
- default void onAssociationChanged(
- @ChangeType int changeType, AssociationInfo association) {
- switch (changeType) {
- case CHANGE_TYPE_ADDED:
- onAssociationAdded(association);
- break;
-
- case CHANGE_TYPE_REMOVED:
- onAssociationRemoved(association);
- break;
-
- case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
- onAssociationUpdated(association, true);
- break;
-
- case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
- onAssociationUpdated(association, false);
- break;
- }
- }
-
- default void onAssociationAdded(AssociationInfo association) {}
-
- default void onAssociationRemoved(AssociationInfo association) {}
-
- default void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {}
- }
-
- /**
- * @return all CDM associations.
- */
- @NonNull
- Collection<AssociationInfo> getAssociations();
-
- /**
- * @return a {@link List} of associations that belong to the user.
- */
- @NonNull
- List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId);
-
- /**
- * @return a {@link List} of association that belong to the package.
- */
- @NonNull
- List<AssociationInfo> getAssociationsForPackage(
- @UserIdInt int userId, @NonNull String packageName);
-
- /**
- * @return an association with the given address that belong to the given package if such an
- * association exists, otherwise {@code null}.
- */
- @Nullable
- AssociationInfo getAssociationsForPackageWithAddress(
- @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress);
-
- /**
- * @return an association with the given id if such an association exists, otherwise
- * {@code null}.
- */
- @Nullable
- AssociationInfo getAssociationById(int id);
-
- /**
- * @return all associations with the given MAc address.
- */
- @NonNull
- List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress);
-
- /** Register a {@link OnChangeListener} */
- void registerListener(@NonNull OnChangeListener listener);
-
- /** Un-register a previously registered {@link OnChangeListener} */
- void unregisterListener(@NonNull OnChangeListener listener);
-
- /** @hide */
- static String changeTypeToString(@ChangeType int changeType) {
- switch (changeType) {
- case CHANGE_TYPE_ADDED:
- return "ASSOCIATION_ADDED";
-
- case CHANGE_TYPE_REMOVED:
- return "ASSOCIATION_REMOVED";
-
- case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
- return "ASSOCIATION_UPDATED";
-
- case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
- return "ASSOCIATION_UPDATED_ADDRESS_UNCHANGED";
-
- default:
- return "Unknown (" + changeType + ")";
- }
- }
-}
diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
index e4cc1f8949b5..f2409fb8843e 100644
--- a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
+++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
@@ -34,6 +34,9 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.CollectionUtils;
+import com.android.server.companion.association.AssociationDiskStore;
+import com.android.server.companion.association.AssociationRequestsProcessor;
+import com.android.server.companion.association.AssociationStore;
import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
import java.nio.ByteBuffer;
@@ -54,9 +57,9 @@ class BackupRestoreProcessor {
@NonNull
private final PackageManagerInternal mPackageManager;
@NonNull
- private final AssociationStoreImpl mAssociationStore;
+ private final AssociationStore mAssociationStore;
@NonNull
- private final PersistentDataStore mPersistentStore;
+ private final AssociationDiskStore mPersistentStore;
@NonNull
private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
@NonNull
@@ -71,8 +74,8 @@ class BackupRestoreProcessor {
new PerUserAssociationSet();
BackupRestoreProcessor(@NonNull CompanionDeviceManagerService service,
- @NonNull AssociationStoreImpl associationStore,
- @NonNull PersistentDataStore persistentStore,
+ @NonNull AssociationStore associationStore,
+ @NonNull AssociationDiskStore persistentStore,
@NonNull SystemDataTransferRequestStore systemDataTransferRequestStore,
@NonNull AssociationRequestsProcessor associationRequestsProcessor) {
mService = service;
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index af0777c74605..c801489ce963 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -37,7 +37,11 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.PerUser;
+import com.android.server.companion.association.AssociationStore;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
+import com.android.server.companion.presence.ObservableUuidStore;
+import com.android.server.companion.utils.PackageUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -61,7 +65,7 @@ import java.util.Map;
* <ul>
* <li> {@link #bindCompanionApplication(int, String, boolean)}
* <li> {@link #unbindCompanionApplication(int, String)}
- * <li> {@link #notifyCompanionApplicationDevicePresenceEvent(AssociationInfo, int)}
+ * <li> {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)}
* <li> {@link #isCompanionApplicationBound(int, String)}
* <li> {@link #isRebindingCompanionApplicationScheduled(int, String)}
* </ul>
@@ -251,7 +255,13 @@ public class CompanionApplicationController {
serviceConnector.connect();
}
- void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
+ /**
+ * Notify the app that the device appeared.
+ *
+ * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead
+ */
+ @Deprecated
+ public void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
final int userId = association.getUserId();
final String packageName = association.getPackageName();
@@ -273,7 +283,13 @@ public class CompanionApplicationController {
primaryServiceConnector.postOnDeviceAppeared(association);
}
- void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
+ /**
+ * Notify the app that the device disappeared.
+ *
+ * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead
+ */
+ @Deprecated
+ public void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
final int userId = association.getUserId();
final String packageName = association.getPackageName();
@@ -295,7 +311,10 @@ public class CompanionApplicationController {
primaryServiceConnector.postOnDeviceDisappeared(association);
}
- void notifyCompanionApplicationDevicePresenceEvent(AssociationInfo association, int event) {
+ /**
+ * Notify the app that the device appeared.
+ */
+ public void notifyCompanionDevicePresenceEvent(AssociationInfo association, int event) {
final int userId = association.getUserId();
final String packageName = association.getPackageName();
final CompanionDeviceServiceConnector primaryServiceConnector =
@@ -318,7 +337,10 @@ public class CompanionApplicationController {
primaryServiceConnector.postOnDevicePresenceEvent(devicePresenceEvent);
}
- void notifyApplicationDevicePresenceEvent(ObservableUuid uuid, int event) {
+ /**
+ * Notify the app that the device disappeared.
+ */
+ public void notifyUuidDevicePresenceEvent(ObservableUuid uuid, int event) {
final int userId = uuid.getUserId();
final ParcelUuid parcelUuid = uuid.getUuid();
final String packageName = uuid.getPackageName();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 09c77939eb7b..e4a1048e9faa 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -37,16 +37,18 @@ import static android.os.UserHandle.getCallingUserId;
import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
-import static com.android.server.companion.PackageUtils.isRestrictedSettingsAllowed;
-import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.PackageUtils.getPackageInfo;
-import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
-import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
-import static com.android.server.companion.PermissionsUtils.enforceCallerCanObservingDevicePresenceByUuid;
-import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
-import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
-import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+import static com.android.server.companion.association.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
+import static com.android.server.companion.utils.AssociationUtils.getFirstAssociationIdForUser;
+import static com.android.server.companion.utils.AssociationUtils.getLastAssociationIdForUser;
+import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
+import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
+import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
+import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageCompanionDevice;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanObservingDevicePresenceByUuid;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
+import static com.android.server.companion.utils.PermissionsUtils.sanitizeWithCallerChecks;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.DAYS;
@@ -117,12 +119,19 @@ import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.companion.association.AssociationDiskStore;
+import com.android.server.companion.association.AssociationRequestsProcessor;
+import com.android.server.companion.association.AssociationRevokeProcessor;
+import com.android.server.companion.association.AssociationStore;
+import com.android.server.companion.association.InactiveAssociationsRemovalService;
import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall;
import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
+import com.android.server.companion.presence.ObservableUuidStore;
import com.android.server.companion.transport.CompanionTransportManager;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -145,8 +154,6 @@ public class CompanionDeviceManagerService extends SystemService {
static final String TAG = "CDM_CompanionDeviceManagerService";
static final boolean DEBUG = false;
- /** Range of Association IDs allocated for a user. */
- private static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000;
private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
@@ -158,10 +165,10 @@ public class CompanionDeviceManagerService extends SystemService {
private static final int MAX_CN_LENGTH = 500;
private final ActivityManager mActivityManager;
- private PersistentDataStore mPersistentStore;
+ private AssociationDiskStore mAssociationDiskStore;
private final PersistUserStateHandler mUserPersistenceHandler;
- private final AssociationStoreImpl mAssociationStore;
+ private final AssociationStore mAssociationStore;
private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
private AssociationRequestsProcessor mAssociationRequestsProcessor;
private SystemDataTransferProcessor mSystemDataTransferProcessor;
@@ -176,7 +183,7 @@ public class CompanionDeviceManagerService extends SystemService {
private final IAppOpsService mAppOpsManager;
private final PowerWhitelistManager mPowerWhitelistManager;
private final UserManager mUserManager;
- final PackageManagerInternal mPackageManagerInternal;
+ public final PackageManagerInternal mPackageManagerInternal;
private final PowerManagerInternal mPowerManagerInternal;
/**
@@ -208,7 +215,7 @@ public class CompanionDeviceManagerService extends SystemService {
mUserManager = context.getSystemService(UserManager.class);
mUserPersistenceHandler = new PersistUserStateHandler();
- mAssociationStore = new AssociationStoreImpl();
+ mAssociationStore = new AssociationStore();
mSystemDataTransferRequestStore = new SystemDataTransferRequestStore();
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -219,15 +226,13 @@ public class CompanionDeviceManagerService extends SystemService {
public void onStart() {
final Context context = getContext();
- mPersistentStore = new PersistentDataStore();
+ mAssociationDiskStore = new AssociationDiskStore();
mAssociationRequestsProcessor = new AssociationRequestsProcessor(
/* cdmService */ this, mAssociationStore);
mBackupRestoreProcessor = new BackupRestoreProcessor(
- /* cdmService */ this, mAssociationStore, mPersistentStore,
+ /* cdmService */ this, mAssociationStore, mAssociationDiskStore,
mSystemDataTransferRequestStore, mAssociationRequestsProcessor);
- loadAssociationsFromDisk();
-
mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId());
mAssociationStore.registerListener(mAssociationStoreChangeListener);
@@ -238,13 +243,18 @@ public class CompanionDeviceManagerService extends SystemService {
mCompanionAppController = new CompanionApplicationController(
context, mAssociationStore, mObservableUuidStore, mDevicePresenceMonitor,
mPowerManagerInternal);
+
+ mAssociationRevokeProcessor = new AssociationRevokeProcessor(this, mAssociationStore,
+ mPackageManagerInternal, mDevicePresenceMonitor, mCompanionAppController,
+ mSystemDataTransferRequestStore);
+
+ loadAssociationsFromDisk();
+
mTransportManager = new CompanionTransportManager(context, mAssociationStore);
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
mPackageManagerInternal, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
- mAssociationRevokeProcessor = new AssociationRevokeProcessor(this, mAssociationStore,
- mPackageManagerInternal, mDevicePresenceMonitor, mCompanionAppController,
- mSystemDataTransferRequestStore);
+
// TODO(b/279663946): move context sync to a dedicated system service
mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager);
@@ -259,10 +269,13 @@ public class CompanionDeviceManagerService extends SystemService {
void loadAssociationsFromDisk() {
final Set<AssociationInfo> allAssociations = new ArraySet<>();
synchronized (mPreviouslyUsedIds) {
+ List<Integer> userIds = new ArrayList<>();
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ userIds.add(user.id);
+ }
// The data is stored in DE directories, so we can read the data for all users now
// (which would not be possible if the data was stored to CE directories).
- mPersistentStore.readStateForUsers(
- mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
+ mAssociationDiskStore.readStateForUsers(userIds, allAssociations, mPreviouslyUsedIds);
}
final Set<AssociationInfo> activeAssociations =
@@ -286,7 +299,7 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
- mAssociationStore.setAssociations(activeAssociations);
+ mAssociationStore.setAssociationsToCache(activeAssociations);
// IMPORTANT: only do this AFTER mAssociationStore.setAssociations(), because
// persistStateForUser() queries AssociationStore.
@@ -435,7 +448,7 @@ public class CompanionDeviceManagerService extends SystemService {
bindApplicationIfNeeded(association);
- mCompanionAppController.notifyCompanionApplicationDevicePresenceEvent(
+ mCompanionAppController.notifyCompanionDevicePresenceEvent(
association, event);
break;
case EVENT_BLE_DISAPPEARED:
@@ -446,7 +459,7 @@ public class CompanionDeviceManagerService extends SystemService {
return;
}
if (association.shouldBindWhenPresent()) {
- mCompanionAppController.notifyCompanionApplicationDevicePresenceEvent(
+ mCompanionAppController.notifyCompanionDevicePresenceEvent(
association, event);
}
// Check if there are other devices associated to the app that are present.
@@ -475,7 +488,7 @@ public class CompanionDeviceManagerService extends SystemService {
Log.i(TAG, "u" + userId + "\\" + packageName + " is already bound");
}
- mCompanionAppController.notifyApplicationDevicePresenceEvent(uuid, event);
+ mCompanionAppController.notifyUuidDevicePresenceEvent(uuid, event);
break;
case EVENT_BT_DISCONNECTED:
@@ -484,7 +497,7 @@ public class CompanionDeviceManagerService extends SystemService {
return;
}
- mCompanionAppController.notifyApplicationDevicePresenceEvent(uuid, event);
+ mCompanionAppController.notifyUuidDevicePresenceEvent(uuid, event);
// Check if there are other devices associated to the app or the UUID to be
// observed are present.
if (shouldBindPackage(userId, packageName)) return;
@@ -577,7 +590,7 @@ public class CompanionDeviceManagerService extends SystemService {
final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
- mPersistentStore.persistStateForUser(userId, allAssociations, usedIdsForUser);
+ mAssociationDiskStore.persistStateForUser(userId, allAssociations, usedIdsForUser);
}
private void notifyListeners(
@@ -641,7 +654,8 @@ public class CompanionDeviceManagerService extends SystemService {
final List<AssociationInfo> associationsForPackage =
mAssociationStore.getAssociationsForPackage(userId, packageName);
for (AssociationInfo association : associationsForPackage) {
- updateSpecialAccessPermissionForAssociatedPackage(association);
+ updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
+ association.getPackageName());
}
mCompanionAppController.onPackagesChanged(userId);
@@ -687,7 +701,7 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
- class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub {
+ public class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub {
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -1333,7 +1347,10 @@ public class CompanionDeviceManagerService extends SystemService {
return usedIdsForPackage;
}
- int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) {
+ /**
+ * Get a new association id for the package.
+ */
+ public int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) {
synchronized (mPreviouslyUsedIds) {
// First: collect all IDs currently in use for this user's Associations.
final SparseBooleanArray usedIds = new SparseBooleanArray();
@@ -1378,9 +1395,12 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
- void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
+ /**
+ * Update special access for the association's package
+ */
+ public void updateSpecialAccessPermissionForAssociatedPackage(int userId, String packageName) {
final PackageInfo packageInfo =
- getPackageInfo(getContext(), association.getUserId(), association.getPackageName());
+ getPackageInfo(getContext(), userId, packageName);
Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo));
}
@@ -1534,15 +1554,6 @@ public class CompanionDeviceManagerService extends SystemService {
}
};
- static int getFirstAssociationIdForUser(@UserIdInt int userId) {
- // We want the IDs to start from 1, not 0.
- return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1;
- }
-
- static int getLastAssociationIdForUser(@UserIdInt int userId) {
- return (userId + 1) * ASSOCIATIONS_IDS_PER_USER_RANGE;
- }
-
private static Map<String, Set<Integer>> deepUnmodifiableCopy(Map<String, Set<Integer>> orig) {
final Map<String, Set<Integer>> copy = new HashMap<>();
@@ -1666,11 +1677,17 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
- void postPersistUserState(@UserIdInt int userId) {
+ /**
+ * Persist associations
+ */
+ public void postPersistUserState(@UserIdInt int userId) {
mUserPersistenceHandler.postPersistUserState(userId);
}
- static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> {
+ /**
+ * Set to store associations
+ */
+ public static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> {
@Override
protected @NonNull Set<AssociationInfo> create(int userId) {
return new ArraySet<>();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index de4f2b60170f..16877dcaf183 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -18,7 +18,7 @@ package com.android.server.companion;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
-import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+import static com.android.server.companion.utils.PermissionsUtils.sanitizeWithCallerChecks;
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
@@ -32,10 +32,14 @@ import android.os.ShellCommand;
import android.util.Base64;
import android.util.proto.ProtoOutputStream;
+import com.android.server.companion.association.AssociationRequestsProcessor;
+import com.android.server.companion.association.AssociationRevokeProcessor;
+import com.android.server.companion.association.AssociationStore;
import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
import com.android.server.companion.datatransfer.contextsync.BitmapUtils;
import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
import com.android.server.companion.transport.CompanionTransportManager;
import java.io.PrintWriter;
@@ -46,7 +50,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
private final CompanionDeviceManagerService mService;
private final AssociationRevokeProcessor mRevokeProcessor;
- private final AssociationStoreImpl mAssociationStore;
+ private final AssociationStore mAssociationStore;
private final CompanionDevicePresenceMonitor mDevicePresenceMonitor;
private final CompanionTransportManager mTransportManager;
@@ -55,7 +59,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
private final BackupRestoreProcessor mBackupRestoreProcessor;
CompanionDeviceShellCommand(CompanionDeviceManagerService service,
- AssociationStoreImpl associationStore,
+ AssociationStore associationStore,
CompanionDevicePresenceMonitor devicePresenceMonitor,
CompanionTransportManager transportManager,
SystemDataTransferProcessor systemDataTransferProcessor,
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index 1ebe65c6aa5f..75cb12058247 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.association;
import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -25,25 +25,23 @@ import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.companion.CompanionDeviceManagerService.getFirstAssociationIdForUser;
-import static com.android.server.companion.CompanionDeviceManagerService.getLastAssociationIdForUser;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.fileToByteArray;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.AssociationUtils.getFirstAssociationIdForUser;
+import static com.android.server.companion.utils.AssociationUtils.getLastAssociationIdForUser;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.fileToByteArray;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
-import android.content.pm.UserInfo;
import android.net.MacAddress;
import android.os.Environment;
import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -70,6 +68,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
+ * IMPORTANT: This class should NOT be directly used except {@link AssociationStore}
+ *
* The class responsible for persisting Association records and other related information (such as
* previously used IDs) to a disk, and reading the data back from the disk.
*
@@ -106,8 +106,6 @@ import java.util.concurrent.ConcurrentMap;
* Since Android T the data is stored to "companion_device_manager.xml" file in
* {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}.
*
- * See {@link DataStoreUtils#getBaseStorageFileForUser(int, String)}
- *
* <p>
* Since Android T the data is stored using the v1 schema.
*
@@ -160,9 +158,8 @@ import java.util.concurrent.ConcurrentMap;
* }</pre>
*/
@SuppressLint("LongLogTag")
-final class PersistentDataStore {
- private static final String TAG = "CompanionDevice_PersistentDataStore";
- private static final boolean DEBUG = CompanionDeviceManagerService.DEBUG;
+public final class AssociationDiskStore {
+ private static final String TAG = "CompanionDevice_AssociationDiskStore";
private static final int CURRENT_PERSISTENCE_VERSION = 1;
@@ -199,11 +196,13 @@ final class PersistentDataStore {
private final @NonNull ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
new ConcurrentHashMap<>();
- void readStateForUsers(@NonNull List<UserInfo> users,
+ /**
+ * Read all associations for given users
+ */
+ public void readStateForUsers(@NonNull List<Integer> userIds,
@NonNull Set<AssociationInfo> allAssociationsOut,
@NonNull SparseArray<Map<String, Set<Integer>>> previouslyUsedIdsPerUserOut) {
- for (UserInfo user : users) {
- final int userId = user.id;
+ for (int userId : userIds) {
// Previously used IDs are stored in the "out" collection per-user.
final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
@@ -246,12 +245,11 @@ final class PersistentDataStore {
* @param associationsOut a container to read the {@link AssociationInfo}s "into".
* @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
*/
- void readStateForUser(@UserIdInt int userId,
+ private void readStateForUser(@UserIdInt int userId,
@NonNull Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
Slog.i(TAG, "Reading associations for user " + userId + " from disk");
final AtomicFile file = getStorageFileForUser(userId);
- if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath());
// getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
// accesses to the file on the file system using this AtomicFile object.
@@ -260,12 +258,8 @@ final class PersistentDataStore {
final AtomicFile readFrom;
final String rootTag;
if (!file.getBaseFile().exists()) {
- if (DEBUG) Log.d(TAG, " > File does not exist -> Try to read legacy file");
-
legacyBaseFile = getBaseLegacyStorageFileForUser(userId);
- if (DEBUG) Log.d(TAG, " > Legacy file=" + legacyBaseFile.getPath());
if (!legacyBaseFile.exists()) {
- if (DEBUG) Log.d(TAG, " > Legacy file does not exist -> Abort");
return;
}
@@ -276,27 +270,16 @@ final class PersistentDataStore {
rootTag = XML_TAG_STATE;
}
- if (DEBUG) Log.d(TAG, " > Reading associations...");
final int version = readStateFromFileLocked(userId, readFrom, rootTag,
associationsOut, previouslyUsedIdsPerPackageOut);
- if (DEBUG) {
- Log.d(TAG, " > Done reading: " + associationsOut);
- if (version < CURRENT_PERSISTENCE_VERSION) {
- Log.d(TAG, " > File used old format: v." + version + " -> Re-write");
- }
- }
if (legacyBaseFile != null || version < CURRENT_PERSISTENCE_VERSION) {
// The data is either in the legacy file or in the legacy format, or both.
// Save the data to right file in using the current format.
- if (DEBUG) {
- Log.d(TAG, " > Writing the data to " + file.getBaseFile().getPath());
- }
persistStateToFileLocked(file, associationsOut, previouslyUsedIdsPerPackageOut);
if (legacyBaseFile != null) {
// We saved the data to the right file, can delete the old file now.
- if (DEBUG) Log.d(TAG, " > Deleting legacy file");
legacyBaseFile.delete();
}
}
@@ -313,14 +296,12 @@ final class PersistentDataStore {
* @param associations a set of user's associations.
* @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
*/
- void persistStateForUser(@UserIdInt int userId,
+ public void persistStateForUser(@UserIdInt int userId,
@NonNull Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
Slog.i(TAG, "Writing associations for user " + userId + " to disk");
- if (DEBUG) Slog.d(TAG, " > " + associations);
final AtomicFile file = getStorageFileForUser(userId);
- if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath());
// getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
// accesses to the file on the file system using this AtomicFile object.
synchronized (file) {
@@ -403,7 +384,10 @@ final class PersistentDataStore {
u -> createStorageFileForUser(userId, FILE_NAME));
}
- byte[] getBackupPayload(@UserIdInt int userId) {
+ /**
+ * Get associations backup payload from disk
+ */
+ public byte[] getBackupPayload(@UserIdInt int userId) {
Slog.i(TAG, "Fetching stored state data for user " + userId + " from disk");
final AtomicFile file = getStorageFileForUser(userId);
@@ -412,7 +396,10 @@ final class PersistentDataStore {
}
}
- void readStateFromPayload(byte[] payload, @UserIdInt int userId,
+ /**
+ * Convert payload to a set of associations
+ */
+ public void readStateFromPayload(byte[] payload, @UserIdInt int userId,
@NonNull Set<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
try (ByteArrayInputStream in = new ByteArrayInputStream(payload)) {
@@ -614,7 +601,7 @@ final class PersistentDataStore {
macAddress, displayName, profile, null, selfManaged, notify,
revoked, pending, timeApproved, lastTimeConnected, systemDataSyncFlags);
} catch (Exception e) {
- if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
+ Slog.e(TAG, "Could not create AssociationInfo", e);
}
return associationInfo;
}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index d0eb59d83f5a..29ec7c2c9743 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.association;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
@@ -24,13 +24,12 @@ import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR;
import static android.content.ComponentName.createRelative;
import static android.content.pm.PackageManager.FEATURE_WATCH;
-import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.MetricUtils.logCreateAssociation;
-import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
-import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
-import static com.android.server.companion.RolesUtils.isRoleHolder;
-import static com.android.server.companion.Utils.prepareForIpc;
+import static com.android.server.companion.utils.MetricUtils.logCreateAssociation;
+import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
+import static com.android.server.companion.utils.PermissionsUtils.enforcePermissionForCreatingAssociation;
+import static com.android.server.companion.utils.RolesUtils.addRoleHolderForAssociation;
+import static com.android.server.companion.utils.RolesUtils.isRoleHolder;
+import static com.android.server.companion.utils.Utils.prepareForIpc;
import static java.util.Objects.requireNonNull;
@@ -59,6 +58,8 @@ import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.R;
+import com.android.server.companion.CompanionDeviceManagerService;
+import com.android.server.companion.utils.PackageUtils;
import java.util.List;
@@ -106,7 +107,7 @@ import java.util.List;
* ResultReceiver, MacAddress)
*/
@SuppressLint("LongLogTag")
-class AssociationRequestsProcessor {
+public class AssociationRequestsProcessor {
private static final String TAG = "CDM_AssociationRequestsProcessor";
// AssociationRequestsProcessor <-> UI
@@ -129,12 +130,12 @@ class AssociationRequestsProcessor {
private final @NonNull Context mContext;
private final @NonNull CompanionDeviceManagerService mService;
private final @NonNull PackageManagerInternal mPackageManager;
- private final @NonNull AssociationStoreImpl mAssociationStore;
+ private final @NonNull AssociationStore mAssociationStore;
@NonNull
private final ComponentName mCompanionDeviceActivity;
- AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service,
- @NonNull AssociationStoreImpl associationStore) {
+ public AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service,
+ @NonNull AssociationStore associationStore) {
mContext = service.getContext();
mService = service;
mPackageManager = service.mPackageManagerInternal;
@@ -148,7 +149,7 @@ class AssociationRequestsProcessor {
* Handle incoming {@link AssociationRequest}s, sent via
* {@link android.companion.ICompanionDeviceManager#associate(AssociationRequest, IAssociationRequestCallback, String, int)}
*/
- void processNewAssociationRequest(@NonNull AssociationRequest request,
+ public void processNewAssociationRequest(@NonNull AssociationRequest request,
@NonNull String packageName, @UserIdInt int userId,
@NonNull IAssociationRequestCallback callback) {
requireNonNull(request, "Request MUST NOT be null");
@@ -160,14 +161,11 @@ class AssociationRequestsProcessor {
requireNonNull(callback, "Callback MUST NOT be null");
final int packageUid = mPackageManager.getPackageUid(packageName, 0, userId);
- if (DEBUG) {
- Slog.d(TAG, "processNewAssociationRequest() "
- + "request=" + request + ", "
- + "package=u" + userId + "/" + packageName + " (uid=" + packageUid + ")");
- }
+ Slog.d(TAG, "processNewAssociationRequest() " + "request=" + request + ", " + "package=u"
+ + userId + "/" + packageName + " (uid=" + packageUid + ")");
// 1. Enforce permissions and other requirements.
- enforcePermissionsForAssociation(mContext, request, packageUid);
+ enforcePermissionForCreatingAssociation(mContext, request, packageUid);
enforceUsesCompanionDeviceFeature(mContext, userId, packageName);
// 2a. Check if association can be created without launching UI (i.e. CDM needs NEITHER
@@ -222,7 +220,7 @@ class AssociationRequestsProcessor {
/**
* Process another AssociationRequest in CompanionDeviceActivity to cancel current dialog.
*/
- PendingIntent buildAssociationCancellationIntent(@NonNull String packageName,
+ public PendingIntent buildAssociationCancellationIntent(@NonNull String packageName,
@UserIdInt int userId) {
requireNonNull(packageName, "Package name MUST NOT be null");
@@ -247,17 +245,10 @@ class AssociationRequestsProcessor {
final int userId = request.getUserId();
final int packageUid = mPackageManager.getPackageUid(packageName, 0, userId);
- if (DEBUG) {
- Slog.d(TAG, "processAssociationRequestApproval()\n"
- + " package=u" + userId + "/" + packageName + " (uid=" + packageUid + ")\n"
- + " request=" + request + "\n"
- + " macAddress=" + macAddress + "\n");
- }
-
// 1. Need to check permissions again in case something changed, since we first received
// this request.
try {
- enforcePermissionsForAssociation(mContext, request, packageUid);
+ enforcePermissionForCreatingAssociation(mContext, request, packageUid);
} catch (SecurityException e) {
// Since, at this point the caller is our own UI, we need to catch the exception on
// forward it back to the application via the callback.
@@ -287,6 +278,9 @@ class AssociationRequestsProcessor {
}
}
+ /**
+ * Create an association.
+ */
public void createAssociation(@UserIdInt int userId, @NonNull String packageName,
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
@Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
@@ -308,6 +302,9 @@ class AssociationRequestsProcessor {
// that there are other devices with the same profile, so the role holder won't be removed.
}
+ /**
+ * Grant a role if specified and add an association to store.
+ */
public void maybeGrantRoleAndStoreAssociation(@NonNull AssociationInfo association,
@Nullable IAssociationRequestCallback callback,
@Nullable ResultReceiver resultReceiver) {
@@ -316,6 +313,9 @@ class AssociationRequestsProcessor {
// If it is null, then the operation will succeed without granting any role.
addRoleHolderForAssociation(mService.getContext(), association, success -> {
if (success) {
+ Slog.i(TAG, "Added " + association.getDeviceProfile() + " role to userId="
+ + association.getUserId() + ", packageName="
+ + association.getPackageName());
addAssociationToStore(association);
sendCallbackAndFinish(association, callback, resultReceiver);
} else {
@@ -327,6 +327,9 @@ class AssociationRequestsProcessor {
});
}
+ /**
+ * Enable system data sync.
+ */
public void enableSystemDataSync(int associationId, int flags) {
AssociationInfo association = mAssociationStore.getAssociationById(associationId);
AssociationInfo updated = (new AssociationInfo.Builder(association))
@@ -334,6 +337,9 @@ class AssociationRequestsProcessor {
mAssociationStore.updateAssociation(updated);
}
+ /**
+ * Disable system data sync.
+ */
public void disableSystemDataSync(int associationId, int flags) {
AssociationInfo association = mAssociationStore.getAssociationById(associationId);
AssociationInfo updated = (new AssociationInfo.Builder(association))
@@ -346,7 +352,8 @@ class AssociationRequestsProcessor {
mAssociationStore.addAssociation(association);
- mService.updateSpecialAccessPermissionForAssociatedPackage(association);
+ mService.updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
+ association.getPackageName());
logCreateAssociation(association.getDeviceProfile());
}
@@ -427,38 +434,37 @@ class AssociationRequestsProcessor {
private final ResultReceiver mOnRequestConfirmationReceiver =
new ResultReceiver(Handler.getMain()) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle data) {
- if (DEBUG) {
- Slog.d(TAG, "mOnRequestConfirmationReceiver.onReceiveResult() "
- + "code=" + resultCode + ", " + "data=" + data);
- }
-
- if (resultCode != RESULT_CODE_ASSOCIATION_APPROVED) {
- Slog.w(TAG, "Unknown result code:" + resultCode);
- return;
- }
-
- final AssociationRequest request = data.getParcelable(EXTRA_ASSOCIATION_REQUEST, android.companion.AssociationRequest.class);
- final IAssociationRequestCallback callback = IAssociationRequestCallback.Stub
- .asInterface(data.getBinder(EXTRA_APPLICATION_CALLBACK));
- final ResultReceiver resultReceiver = data.getParcelable(EXTRA_RESULT_RECEIVER, android.os.ResultReceiver.class);
-
- requireNonNull(request);
- requireNonNull(callback);
- requireNonNull(resultReceiver);
-
- final MacAddress macAddress;
- if (request.isSelfManaged()) {
- macAddress = null;
- } else {
- macAddress = data.getParcelable(EXTRA_MAC_ADDRESS, android.net.MacAddress.class);
- requireNonNull(macAddress);
- }
-
- processAssociationRequestApproval(request, callback, resultReceiver, macAddress);
- }
- };
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle data) {
+ if (resultCode != RESULT_CODE_ASSOCIATION_APPROVED) {
+ Slog.w(TAG, "Unknown result code:" + resultCode);
+ return;
+ }
+
+ final AssociationRequest request = data.getParcelable(EXTRA_ASSOCIATION_REQUEST,
+ android.companion.AssociationRequest.class);
+ final IAssociationRequestCallback callback = IAssociationRequestCallback.Stub
+ .asInterface(data.getBinder(EXTRA_APPLICATION_CALLBACK));
+ final ResultReceiver resultReceiver = data.getParcelable(EXTRA_RESULT_RECEIVER,
+ android.os.ResultReceiver.class);
+
+ requireNonNull(request);
+ requireNonNull(callback);
+ requireNonNull(resultReceiver);
+
+ final MacAddress macAddress;
+ if (request.isSelfManaged()) {
+ macAddress = null;
+ } else {
+ macAddress = data.getParcelable(EXTRA_MAC_ADDRESS,
+ android.net.MacAddress.class);
+ requireNonNull(macAddress);
+ }
+
+ processAssociationRequestApproval(request, callback, resultReceiver,
+ macAddress);
+ }
+ };
private boolean mayAssociateWithoutPrompt(@NonNull String packageName, @UserIdInt int userId) {
// Throttle frequent associations
diff --git a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRevokeProcessor.java
index de6382e316df..490be0da593b 100644
--- a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRevokeProcessor.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.association;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
import static com.android.internal.util.CollectionUtils.any;
-import static com.android.server.companion.MetricUtils.logRemoveAssociation;
-import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
+import static com.android.server.companion.utils.MetricUtils.logRemoveAssociation;
+import static com.android.server.companion.utils.RolesUtils.removeRoleHolderForAssociation;
import static com.android.server.companion.CompanionDeviceManagerService.PerUserAssociationSet;
import android.annotation.NonNull;
@@ -38,6 +38,8 @@ import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.companion.CompanionApplicationController;
+import com.android.server.companion.CompanionDeviceManagerService;
import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
@@ -55,7 +57,7 @@ public class AssociationRevokeProcessor {
private static final boolean DEBUG = false;
private final @NonNull Context mContext;
private final @NonNull CompanionDeviceManagerService mService;
- private final @NonNull AssociationStoreImpl mAssociationStore;
+ private final @NonNull AssociationStore mAssociationStore;
private final @NonNull PackageManagerInternal mPackageManagerInternal;
private final @NonNull CompanionDevicePresenceMonitor mDevicePresenceMonitor;
private final @NonNull SystemDataTransferRequestStore mSystemDataTransferRequestStore;
@@ -90,8 +92,8 @@ public class AssociationRevokeProcessor {
@GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval")
private final Map<Integer, String> mUidsPendingRoleHolderRemoval = new HashMap<>();
- AssociationRevokeProcessor(@NonNull CompanionDeviceManagerService service,
- @NonNull AssociationStoreImpl associationStore,
+ public AssociationRevokeProcessor(@NonNull CompanionDeviceManagerService service,
+ @NonNull AssociationStore associationStore,
@NonNull PackageManagerInternal packageManager,
@NonNull CompanionDevicePresenceMonitor devicePresenceMonitor,
@NonNull CompanionApplicationController applicationController,
@@ -108,8 +110,11 @@ public class AssociationRevokeProcessor {
mSystemDataTransferRequestStore = systemDataTransferRequestStore;
}
+ /**
+ * Disassociate an association
+ */
// TODO: also revoke notification access
- void disassociateInternal(int associationId) {
+ public void disassociateInternal(int associationId) {
final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
final int userId = association.getUserId();
final String packageName = association.getPackageName();
@@ -168,7 +173,7 @@ public class AssociationRevokeProcessor {
* {@code RoleManager.removeRoleHolderAsUser()} will kill the application's process,
* which would lead to the poor UX, hence need to try later.
*/
- boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) {
+ public boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) {
if (DEBUG) Log.d(TAG, "maybeRemoveRoleHolderForAssociation() association=" + association);
final String deviceProfile = association.getDeviceProfile();
@@ -203,19 +208,11 @@ public class AssociationRevokeProcessor {
return false;
}
- removeRoleHolderForAssociation(mContext, association);
+ removeRoleHolderForAssociation(mContext, association.getUserId(),
+ association.getPackageName(), association.getDeviceProfile());
return true;
}
- @SuppressLint("MissingPermission")
- private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) {
- return Binder.withCleanCallingIdentity(() -> {
- final int uid =
- mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
- return mActivityManager.getUidImportance(uid);
- });
- }
-
/**
* Set revoked flag for active association and add the revoked association and the uid into
* the caches.
@@ -224,7 +221,7 @@ public class AssociationRevokeProcessor {
* @see #mUidsPendingRoleHolderRemoval
* @see OnPackageVisibilityChangeListener
*/
- void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) {
+ public void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) {
// First: set revoked flag
association = (new AssociationInfo.Builder(association)).setRevoked(true).build();
final String packageName = association.getPackageName();
@@ -246,6 +243,28 @@ public class AssociationRevokeProcessor {
}
/**
+ * @return a copy of the revoked associations set (safeguarding against
+ * {@code ConcurrentModificationException}-s).
+ */
+ @NonNull
+ public Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser(
+ @UserIdInt int userId) {
+ synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
+ // Return a copy.
+ return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId));
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) {
+ return Binder.withCleanCallingIdentity(() -> {
+ final int uid =
+ mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
+ return mActivityManager.getUidImportance(uid);
+ });
+ }
+
+ /**
* Remove the revoked association from the cache and also remove the uid from the map if
* there are other associations with the same package still pending for role holder removal.
*
@@ -278,18 +297,6 @@ public class AssociationRevokeProcessor {
}
}
- /**
- * @return a copy of the revoked associations set (safeguarding against
- * {@code ConcurrentModificationException}-s).
- */
- @NonNull Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser(
- @UserIdInt int userId) {
- synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
- // Return a copy.
- return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId));
- }
- }
-
private String getPackageNameByUid(int uid) {
synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
return mUidsPendingRoleHolderRemoval.get(uid);
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java
index 8c6ad3bad857..2f94bdebb988 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.association;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
import android.net.MacAddress;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -30,6 +30,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.CollectionUtils;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -40,24 +42,69 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.StringJoiner;
/**
- * Implementation of the {@link AssociationStore}, with addition of the methods for modification.
- * <ul>
- * <li> {@link #addAssociation(AssociationInfo)}
- * <li> {@link #removeAssociation(int)}
- * <li> {@link #updateAssociation(AssociationInfo)}
- * </ul>
- *
- * The class has package-private access level, and instances of the class should only be created by
- * the {@link CompanionDeviceManagerService}.
- * Other system component (both inside and outside if the com.android.server.companion package)
- * should use public {@link AssociationStore} interface.
+ * Association store for CRUD.
*/
@SuppressLint("LongLogTag")
-class AssociationStoreImpl implements AssociationStore {
- private static final boolean DEBUG = false;
+public class AssociationStore {
+
+ @IntDef(prefix = { "CHANGE_TYPE_" }, value = {
+ CHANGE_TYPE_ADDED,
+ CHANGE_TYPE_REMOVED,
+ CHANGE_TYPE_UPDATED_ADDRESS_CHANGED,
+ CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ChangeType {}
+
+ public static final int CHANGE_TYPE_ADDED = 0;
+ public static final int CHANGE_TYPE_REMOVED = 1;
+ public static final int CHANGE_TYPE_UPDATED_ADDRESS_CHANGED = 2;
+ public static final int CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED = 3;
+
+ /** Listener for any changes to associations. */
+ public interface OnChangeListener {
+ /**
+ * Called when there are association changes.
+ */
+ default void onAssociationChanged(
+ @AssociationStore.ChangeType int changeType, AssociationInfo association) {
+ switch (changeType) {
+ case CHANGE_TYPE_ADDED:
+ onAssociationAdded(association);
+ break;
+
+ case CHANGE_TYPE_REMOVED:
+ onAssociationRemoved(association);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
+ onAssociationUpdated(association, true);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
+ onAssociationUpdated(association, false);
+ break;
+ }
+ }
+
+ /**
+ * Called when an association is added.
+ */
+ default void onAssociationAdded(AssociationInfo association) {}
+
+ /**
+ * Called when an association is removed.
+ */
+ default void onAssociationRemoved(AssociationInfo association) {}
+
+ /**
+ * Called when an association is updated.
+ */
+ default void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {}
+ }
+
private static final String TAG = "CDM_AssociationStore";
private final Object mLock = new Object();
@@ -72,17 +119,17 @@ class AssociationStoreImpl implements AssociationStore {
@GuardedBy("mListeners")
private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
- void addAssociation(@NonNull AssociationInfo association) {
+ /**
+ * Add an association.
+ */
+ public void addAssociation(@NonNull AssociationInfo association) {
+ Slog.i(TAG, "Adding new association=" + association);
+
// Validity check first.
checkNotRevoked(association);
final int id = association.getId();
- if (DEBUG) {
- Log.i(TAG, "addAssociation() " + association.toShortString());
- Log.d(TAG, " association=" + association);
- }
-
synchronized (mLock) {
if (mIdMap.containsKey(id)) {
Slog.e(TAG, "Association with id " + id + " already exists.");
@@ -96,34 +143,34 @@ class AssociationStoreImpl implements AssociationStore {
}
invalidateCacheForUserLocked(association.getUserId());
+
+ Slog.i(TAG, "Done adding new association.");
}
broadcastChange(CHANGE_TYPE_ADDED, association);
}
- void updateAssociation(@NonNull AssociationInfo updated) {
+ /**
+ * Update an association.
+ */
+ public void updateAssociation(@NonNull AssociationInfo updated) {
+ Slog.i(TAG, "Updating new association=" + updated);
// Validity check first.
checkNotRevoked(updated);
final int id = updated.getId();
- if (DEBUG) {
- Log.i(TAG, "updateAssociation() " + updated.toShortString());
- Log.d(TAG, " updated=" + updated);
- }
-
final AssociationInfo current;
final boolean macAddressChanged;
synchronized (mLock) {
current = mIdMap.get(id);
if (current == null) {
- if (DEBUG) Log.w(TAG, "Association with id " + id + " does not exist.");
+ Slog.w(TAG, "Can't update association. It does not exist.");
return;
}
- if (DEBUG) Log.d(TAG, " current=" + current);
if (current.equals(updated)) {
- if (DEBUG) Log.w(TAG, " No changes.");
+ Slog.w(TAG, "Association is the same.");
return;
}
@@ -144,6 +191,7 @@ class AssociationStoreImpl implements AssociationStore {
mAddressMap.computeIfAbsent(updatedAddress, it -> new HashSet<>()).add(id);
}
}
+ Slog.i(TAG, "Done updating association.");
}
final int changeType = macAddressChanged ? CHANGE_TYPE_UPDATED_ADDRESS_CHANGED
@@ -151,21 +199,19 @@ class AssociationStoreImpl implements AssociationStore {
broadcastChange(changeType, updated);
}
- void removeAssociation(int id) {
- if (DEBUG) Log.i(TAG, "removeAssociation() id=" + id);
+ /**
+ * Remove an association
+ */
+ public void removeAssociation(int id) {
+ Slog.i(TAG, "Removing association id=" + id);
final AssociationInfo association;
synchronized (mLock) {
association = mIdMap.remove(id);
if (association == null) {
- if (DEBUG) Log.w(TAG, "Association with id " + id + " is not stored.");
+ Slog.w(TAG, "Can't remove association. It does not exist.");
return;
- } else {
- if (DEBUG) {
- Log.i(TAG, "removed " + association.toShortString());
- Log.d(TAG, " association=" + association);
- }
}
final MacAddress macAddress = association.getDeviceMacAddress();
@@ -174,6 +220,8 @@ class AssociationStoreImpl implements AssociationStore {
}
invalidateCacheForUserLocked(association.getUserId());
+
+ Slog.i(TAG, "Done removing association.");
}
broadcastChange(CHANGE_TYPE_REMOVED, association);
@@ -195,12 +243,18 @@ class AssociationStoreImpl implements AssociationStore {
}
}
+ /**
+ * Get associations for the user.
+ */
public @NonNull List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId) {
synchronized (mLock) {
return getAssociationsForUserLocked(userId);
}
}
+ /**
+ * Get associations for the package
+ */
public @NonNull List<AssociationInfo> getAssociationsForPackage(
@UserIdInt int userId, @NonNull String packageName) {
final List<AssociationInfo> associationsForUser = getAssociationsForUser(userId);
@@ -210,6 +264,9 @@ class AssociationStoreImpl implements AssociationStore {
return Collections.unmodifiableList(associationsForPackage);
}
+ /**
+ * Get associations by mac address for the package.
+ */
public @Nullable AssociationInfo getAssociationsForPackageWithAddress(
@UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
final List<AssociationInfo> associations = getAssociationsByAddress(macAddress);
@@ -217,13 +274,20 @@ class AssociationStoreImpl implements AssociationStore {
it -> it.belongsToPackage(userId, packageName));
}
+ /**
+ * Get association by id.
+ */
public @Nullable AssociationInfo getAssociationById(int id) {
synchronized (mLock) {
return mIdMap.get(id);
}
}
- public @NonNull List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress) {
+ /**
+ * Get associations by mac address.
+ */
+ @NonNull
+ public List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress) {
final MacAddress address = MacAddress.fromString(macAddress);
synchronized (mLock) {
@@ -240,7 +304,8 @@ class AssociationStoreImpl implements AssociationStore {
}
@GuardedBy("mLock")
- private @NonNull List<AssociationInfo> getAssociationsForUserLocked(@UserIdInt int userId) {
+ @NonNull
+ private List<AssociationInfo> getAssociationsForUserLocked(@UserIdInt int userId) {
final List<AssociationInfo> cached = mCachedPerUser.get(userId);
if (cached != null) {
return cached;
@@ -262,12 +327,18 @@ class AssociationStoreImpl implements AssociationStore {
mCachedPerUser.delete(userId);
}
+ /**
+ * Register a listener for association changes.
+ */
public void registerListener(@NonNull OnChangeListener listener) {
synchronized (mListeners) {
mListeners.add(listener);
}
}
+ /**
+ * Unregister a listener previously registered for association changes.
+ */
public void unregisterListener(@NonNull OnChangeListener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
@@ -297,43 +368,30 @@ class AssociationStoreImpl implements AssociationStore {
}
}
- void setAssociations(Collection<AssociationInfo> allAssociations) {
+ /**
+ * Set associations to cache. It will clear the existing cache.
+ */
+ public void setAssociationsToCache(Collection<AssociationInfo> associations) {
// Validity check first.
- allAssociations.forEach(AssociationStoreImpl::checkNotRevoked);
+ associations.forEach(AssociationStore::checkNotRevoked);
- if (DEBUG) {
- Log.i(TAG, "setAssociations() n=" + allAssociations.size());
- final StringJoiner stringJoiner = new StringJoiner(", ");
- allAssociations.forEach(assoc -> stringJoiner.add(assoc.toShortString()));
- Log.v(TAG, " associations=" + stringJoiner);
- }
synchronized (mLock) {
- setAssociationsLocked(allAssociations);
- }
- }
-
- @GuardedBy("mLock")
- private void setAssociationsLocked(Collection<AssociationInfo> associations) {
- clearLocked();
+ mIdMap.clear();
+ mAddressMap.clear();
+ mCachedPerUser.clear();
- for (AssociationInfo association : associations) {
- final int id = association.getId();
- mIdMap.put(id, association);
+ for (AssociationInfo association : associations) {
+ final int id = association.getId();
+ mIdMap.put(id, association);
- final MacAddress address = association.getDeviceMacAddress();
- if (address != null) {
- mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+ final MacAddress address = association.getDeviceMacAddress();
+ if (address != null) {
+ mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+ }
}
}
}
- @GuardedBy("mLock")
- private void clearLocked() {
- mIdMap.clear();
- mAddressMap.clear();
- mCachedPerUser.clear();
- }
-
private static void checkNotRevoked(@NonNull AssociationInfo association) {
if (association.isRevoked()) {
throw new IllegalArgumentException(
diff --git a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java b/services/companion/java/com/android/server/companion/association/InactiveAssociationsRemovalService.java
index aac628cab403..894c49a2b5cf 100644
--- a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java
+++ b/services/companion/java/com/android/server/companion/association/InactiveAssociationsRemovalService.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
-
-import static com.android.server.companion.CompanionDeviceManagerService.TAG;
+package com.android.server.companion.association;
import static java.util.concurrent.TimeUnit.DAYS;
@@ -29,13 +27,17 @@ import android.content.Context;
import android.util.Slog;
import com.android.server.LocalServices;
+import com.android.server.companion.CompanionDeviceManagerServiceInternal;
/**
- * A Job Service responsible for clean up the Association.
+ * A Job Service responsible for clean up idle self-managed associations.
+ *
* The job will be executed only if the device is charging and in idle mode due to the application
- * will be killed if association/role are revoked.
+ * will be killed if association/role are revoked. See {@link AssociationRevokeProcessor}
*/
public class InactiveAssociationsRemovalService extends JobService {
+
+ private static final String TAG = "CDM_InactiveAssociationsRemovalService";
private static final String JOB_NAMESPACE = "companion";
private static final int JOB_ID = 1;
private static final long ONE_DAY_INTERVAL = DAYS.toMillis(1);
@@ -60,7 +62,10 @@ public class InactiveAssociationsRemovalService extends JobService {
return false;
}
- static void schedule(Context context) {
+ /**
+ * Schedule this job.
+ */
+ public static void schedule(Context context) {
Slog.i(TAG, "Scheduling the Association Removal job");
final JobScheduler jobScheduler =
context.getSystemService(JobScheduler.class).forNamespace(JOB_NAMESPACE);
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 260b21f109d0..a08e0da90d49 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -23,7 +23,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PERMISSIO
import static android.content.ComponentName.createRelative;
import static android.content.pm.PackageManager.FEATURE_WATCH;
-import static com.android.server.companion.Utils.prepareForIpc;
+import static com.android.server.companion.utils.Utils.prepareForIpc;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,11 +52,11 @@ import android.permission.PermissionControllerManager;
import android.util.Slog;
import com.android.internal.R;
-import com.android.server.companion.AssociationStore;
import com.android.server.companion.CompanionDeviceManagerService;
-import com.android.server.companion.PackageUtils;
-import com.android.server.companion.PermissionsUtils;
+import com.android.server.companion.association.AssociationStore;
import com.android.server.companion.transport.CompanionTransportManager;
+import com.android.server.companion.utils.PackageUtils;
+import com.android.server.companion.utils.PermissionsUtils;
import java.util.List;
import java.util.concurrent.ExecutorService;
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index c4c80f907b3a..ee816a0c2cc9 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -22,11 +22,11 @@ import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.fileToByteArray;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.fileToByteArray;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 7e3079073c90..99466a966647 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -34,7 +34,7 @@ import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_MATCH_LOST;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
-import static com.android.server.companion.presence.Utils.btDeviceToString;
+import static com.android.server.companion.utils.Utils.btDeviceToString;
import static java.util.Objects.requireNonNull;
@@ -59,8 +59,8 @@ import android.os.Looper;
import android.util.Log;
import android.util.Slog;
-import com.android.server.companion.AssociationStore;
-import com.android.server.companion.AssociationStore.ChangeType;
+import com.android.server.companion.association.AssociationStore;
+import com.android.server.companion.association.AssociationStore.ChangeType;
import java.util.ArrayList;
import java.util.Arrays;
@@ -168,7 +168,7 @@ class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
void startScan() {
enforceInitialized();
- if (DEBUG) Log.i(TAG, "startScan()");
+ Slog.i(TAG, "startBleScan()");
// This method should not be called if scan is already in progress.
if (mScanning) {
Slog.w(TAG, "Scan is already in progress.");
@@ -228,7 +228,7 @@ class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
void stopScanIfNeeded() {
enforceInitialized();
- if (DEBUG) Log.i(TAG, "stopScan()");
+ Slog.i(TAG, "stopBleScan()");
if (!mScanning) {
if (DEBUG) Log.d(TAG, " > not scanning.");
return;
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index c514f3ef29d0..4da3f9bead4e 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -20,7 +20,7 @@ import static android.companion.DevicePresenceEvent.EVENT_BT_CONNECTED;
import static android.companion.DevicePresenceEvent.EVENT_BT_DISCONNECTED;
import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
-import static com.android.server.companion.presence.Utils.btDeviceToString;
+import static com.android.server.companion.utils.Utils.btDeviceToString;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -39,9 +39,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
-import com.android.server.companion.AssociationStore;
-import com.android.server.companion.ObservableUuid;
-import com.android.server.companion.ObservableUuidStore;
+import com.android.server.companion.association.AssociationStore;
import java.util.Arrays;
import java.util.Collections;
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 54a4692d964d..37bbb937d1b5 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -41,10 +41,10 @@ import android.os.UserManager;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
-import com.android.server.companion.AssociationStore;
-import com.android.server.companion.ObservableUuid;
-import com.android.server.companion.ObservableUuidStore;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.companion.association.AssociationStore;
import java.io.PrintWriter;
import java.util.HashSet;
@@ -102,12 +102,24 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>();
private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
private final @NonNull Set<ParcelUuid> mConnectedUuidDevices = new HashSet<>();
+ @GuardedBy("mBtDisconnectedDevices")
+ private final @NonNull Set<Integer> mBtDisconnectedDevices = new HashSet<>();
+
+ // A map to track device presence within 10 seconds of Bluetooth disconnection.
+ // The key is the association ID, and the boolean value indicates if the device
+ // was detected again within that time frame.
+ @GuardedBy("mBtDisconnectedDevices")
+ private final @NonNull SparseBooleanArray mBtDisconnectedDevicesBlePresence =
+ new SparseBooleanArray();
// Tracking "simulated" presence. Used for debugging and testing only.
private final @NonNull Set<Integer> mSimulated = new HashSet<>();
private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper =
new SimulatedDevicePresenceSchedulerHelper();
+ private final BleDeviceDisappearedScheduler mBleDeviceDisappearedScheduler =
+ new BleDeviceDisappearedScheduler();
+
public CompanionDevicePresenceMonitor(UserManager userManager,
@NonNull AssociationStore associationStore,
@NonNull ObservableUuidStore observableUuidStore, @NonNull Callback callback) {
@@ -229,13 +241,24 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
@Override
public void onBluetoothCompanionDeviceConnected(int associationId) {
- Slog.i(TAG, "onBluetoothCompanionDeviceConnected: "
- + "associationId( " + associationId + " )");
- onDevicePresenceEvent(mConnectedBtDevices, associationId, EVENT_BT_CONNECTED);
- // Stop scanning for BLE devices when this device is connected
- // and there are no other devices to connect to.
- if (canStopBleScan()) {
- mBleScanner.stopScanIfNeeded();
+ synchronized (mBtDisconnectedDevices) {
+ // A device is considered reconnected within 10 seconds if a pending BLE lost report is
+ // followed by a detected Bluetooth connection.
+ boolean isReconnected = mBtDisconnectedDevices.contains(associationId);
+ if (isReconnected) {
+ Slog.i(TAG, "Device ( " + associationId + " ) is reconnected within 10s.");
+ mBleDeviceDisappearedScheduler.unScheduleDeviceDisappeared(associationId);
+ }
+
+ Slog.i(TAG, "onBluetoothCompanionDeviceConnected: "
+ + "associationId( " + associationId + " )");
+ onDevicePresenceEvent(mConnectedBtDevices, associationId, EVENT_BT_CONNECTED);
+
+ // Stop the BLE scan if all devices report BT connected status and BLE was present.
+ if (canStopBleScan()) {
+ mBleScanner.stopScanIfNeeded();
+ }
+
}
}
@@ -247,6 +270,14 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
mBleScanner.startScan();
onDevicePresenceEvent(mConnectedBtDevices, associationId, EVENT_BT_DISCONNECTED);
+ // If current device is BLE present but BT is disconnected , means it will be
+ // potentially out of range later. Schedule BLE disappeared callback.
+ if (isBlePresent(associationId)) {
+ synchronized (mBtDisconnectedDevices) {
+ mBtDisconnectedDevices.add(associationId);
+ }
+ mBleDeviceDisappearedScheduler.scheduleBleDeviceDisappeared(associationId);
+ }
}
@Override
@@ -283,6 +314,12 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
@Override
public void onBleCompanionDeviceFound(int associationId) {
onDevicePresenceEvent(mNearbyBleDevices, associationId, EVENT_BLE_APPEARED);
+ synchronized (mBtDisconnectedDevices) {
+ final boolean isCurrentPresent = mBtDisconnectedDevicesBlePresence.get(associationId);
+ if (mBtDisconnectedDevices.contains(associationId) && isCurrentPresent) {
+ mBleDeviceDisappearedScheduler.unScheduleDeviceDisappeared(associationId);
+ }
+ }
}
@Override
@@ -353,6 +390,16 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
switch (event) {
case EVENT_BLE_APPEARED:
+ synchronized (mBtDisconnectedDevices) {
+ // If a BLE device is detected within 10 seconds after BT is disconnected,
+ // flag it as BLE is present.
+ if (mBtDisconnectedDevices.contains(associationId)) {
+ Slog.i(TAG, "Device ( " + associationId + " ) is present,"
+ + " do not need to send the callback with event ( "
+ + EVENT_BLE_APPEARED + " ).");
+ mBtDisconnectedDevicesBlePresence.append(associationId, true);
+ }
+ }
case EVENT_BT_CONNECTED:
case EVENT_SELF_MANAGED_APPEARED:
final boolean added = presentDevicesForSource.add(associationId);
@@ -405,6 +452,8 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
mNearbyBleDevices.remove(id);
mReportedSelfManagedDevices.remove(id);
mSimulated.remove(id);
+ mBtDisconnectedDevices.remove(id);
+ mBtDisconnectedDevicesBlePresence.delete(id);
// Do NOT call mCallback.onDeviceDisappeared()!
// CompanionDeviceManagerService will know that the association is removed, and will do
@@ -427,14 +476,21 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
throw new SecurityException("Caller is neither Shell nor Root");
}
+ /**
+ * The BLE scan can be only stopped if all the devices have been reported
+ * BT connected and BLE presence and are not pending to report BLE lost.
+ */
private boolean canStopBleScan() {
for (AssociationInfo ai : mAssociationStore.getAssociations()) {
int id = ai.getId();
- // The BLE scan cannot be stopped if there's a device is not yet connected.
- if (ai.isNotifyOnDeviceNearby() && !isBtConnected(id)) {
- Slog.i(TAG, "The BLE scan cannot be stopped, "
- + "device( " + id + " ) is not yet connected");
- return false;
+ synchronized (mBtDisconnectedDevices) {
+ if (ai.isNotifyOnDeviceNearby() && !(isBtConnected(id)
+ && isBlePresent(id) && mBtDisconnectedDevices.isEmpty())) {
+ Slog.i(TAG, "The BLE scan cannot be stopped, "
+ + "device( " + id + " ) is not yet connected "
+ + "OR the BLE is not current present Or is pending to report BLE lost");
+ return false;
+ }
}
}
return true;
@@ -514,4 +570,51 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
}
}
}
+
+ private class BleDeviceDisappearedScheduler extends Handler {
+ BleDeviceDisappearedScheduler() {
+ super(Looper.getMainLooper());
+ }
+
+ void scheduleBleDeviceDisappeared(int associationId) {
+ if (hasMessages(associationId)) {
+ removeMessages(associationId);
+ }
+ Slog.i(TAG, "scheduleBleDeviceDisappeared for Device: ( " + associationId + " ).");
+ sendEmptyMessageDelayed(associationId, 10 * 1000 /* 10 seconds */);
+ }
+
+ void unScheduleDeviceDisappeared(int associationId) {
+ if (hasMessages(associationId)) {
+ Slog.i(TAG, "unScheduleDeviceDisappeared for Device( " + associationId + " )");
+ synchronized (mBtDisconnectedDevices) {
+ mBtDisconnectedDevices.remove(associationId);
+ mBtDisconnectedDevicesBlePresence.delete(associationId);
+ }
+
+ removeMessages(associationId);
+ }
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ final int associationId = msg.what;
+ synchronized (mBtDisconnectedDevices) {
+ final boolean isCurrentPresent = mBtDisconnectedDevicesBlePresence.get(
+ associationId);
+ // If a device hasn't reported after 10 seconds and is not currently present,
+ // assume BLE is lost and trigger the onDeviceEvent callback with the
+ // EVENT_BLE_DISAPPEARED event.
+ if (mBtDisconnectedDevices.contains(associationId)
+ && !isCurrentPresent) {
+ Slog.i(TAG, "Device ( " + associationId + " ) is likely BLE out of range, "
+ + "sending callback with event ( " + EVENT_BLE_DISAPPEARED + " )");
+ onDevicePresenceEvent(mNearbyBleDevices, associationId, EVENT_BLE_DISAPPEARED);
+ }
+
+ mBtDisconnectedDevices.remove(associationId);
+ mBtDisconnectedDevicesBlePresence.delete(associationId);
+ }
+ }
+ }
}
diff --git a/services/companion/java/com/android/server/companion/ObservableUuid.java b/services/companion/java/com/android/server/companion/presence/ObservableUuid.java
index 6ab3188c8fd2..9cfa2705cb2f 100644
--- a/services/companion/java/com/android/server/companion/ObservableUuid.java
+++ b/services/companion/java/com/android/server/companion/presence/ObservableUuid.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.presence;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
diff --git a/services/companion/java/com/android/server/companion/ObservableUuidStore.java b/services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
index 94be22afd9e2..ee8b1065b42c 100644
--- a/services/companion/java/com/android/server/companion/ObservableUuidStore.java
+++ b/services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.presence;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -22,10 +22,10 @@ import static com.android.internal.util.XmlUtils.readStringAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -57,6 +57,9 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+/**
+ * This store manages the cache and disk data for observable uuids.
+ */
public class ObservableUuidStore {
private static final String TAG = "CDM_ObservableUuidStore";
private static final String FILE_NAME = "observing_uuids_presence.xml";
@@ -84,9 +87,9 @@ public class ObservableUuidStore {
}
/**
- * Remove the observable uuid from the disk.
+ * Remove the observable uuid.
*/
- void removeObservableUuid(@UserIdInt int userId, ParcelUuid uuid, String packageName) {
+ public void removeObservableUuid(@UserIdInt int userId, ParcelUuid uuid, String packageName) {
List<ObservableUuid> cachedObservableUuids;
synchronized (mLock) {
@@ -101,7 +104,10 @@ public class ObservableUuidStore {
mExecutor.execute(() -> writeObservableUuidToStore(userId, cachedObservableUuids));
}
- void writeObservableUuid(@UserIdInt int userId, ObservableUuid uuid) {
+ /**
+ * Write the observable uuid.
+ */
+ public void writeObservableUuid(@UserIdInt int userId, ObservableUuid uuid) {
Slog.i(TAG, "Writing uuid=" + uuid.getUuid() + " to store.");
List<ObservableUuid> cachedObservableUuids;
diff --git a/services/companion/java/com/android/server/companion/presence/Utils.java b/services/companion/java/com/android/server/companion/presence/Utils.java
deleted file mode 100644
index 583b443c8cb7..000000000000
--- a/services/companion/java/com/android/server/companion/presence/Utils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.presence;
-
-import android.annotation.NonNull;
-import android.bluetooth.BluetoothDevice;
-
-/** Utilities for working with Bluetooth and BLE devices. */
-class Utils {
-
- /**
- * @return short String representation of {@link BluetoothDevice}.
- */
- static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
- final StringBuilder sb = new StringBuilder(btDevice.getAddress());
-
- sb.append(" [name=");
- final String name = btDevice.getName();
- if (name != null) {
- sb.append('\'').append(name).append('\'');
- } else {
- sb.append("null");
- }
-
- final String alias = btDevice.getAlias();
- if (alias != null) {
- sb.append(", alias='").append(alias).append("'");
- }
-
- return sb.append(']').toString();
- }
-
- private Utils() {
- }
-}
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 3861f99eb03c..6dd14ac91a34 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -32,7 +32,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.server.companion.AssociationStore;
+import com.android.server.companion.association.AssociationStore;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/services/companion/java/com/android/server/companion/utils/AssociationUtils.java b/services/companion/java/com/android/server/companion/utils/AssociationUtils.java
new file mode 100644
index 000000000000..e4d96413cf8b
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/utils/AssociationUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.utils;
+
+import android.annotation.UserIdInt;
+
+public final class AssociationUtils {
+
+ /** Range of Association IDs allocated for a user. */
+ private static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000;
+
+ /**
+ * Get the left boundary of the association id range for the user.
+ */
+ public static int getFirstAssociationIdForUser(@UserIdInt int userId) {
+ // We want the IDs to start from 1, not 0.
+ return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1;
+ }
+
+ /**
+ * Get the right boundary of the association id range for the user.
+ */
+ public static int getLastAssociationIdForUser(@UserIdInt int userId) {
+ return (userId + 1) * ASSOCIATIONS_IDS_PER_USER_RANGE;
+ }
+
+ private AssociationUtils() {}
+}
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
index 04ce1f673124..c75b1a57206e 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -80,7 +80,7 @@ public final class DataStoreUtils {
/**
* Writing to file could fail, for example, if the user has been recently removed and so was
- * their DE (/data/system_de/<user-id>/) directory.
+ * their DE (/data/system_de/[user-id]/) directory.
*/
public static void writeToFileSafely(
@NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) {
diff --git a/services/companion/java/com/android/server/companion/MetricUtils.java b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
index cf867b67cbca..8ea5c89116eb 100644
--- a/services/companion/java/com/android/server/companion/MetricUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
@@ -41,7 +41,7 @@ import android.util.ArrayMap;
import java.util.Map;
-final class MetricUtils {
+public final class MetricUtils {
private static final Map<String, Integer> METRIC_DEVICE_PROFILE;
static {
@@ -75,13 +75,19 @@ final class MetricUtils {
METRIC_DEVICE_PROFILE = unmodifiableMap(map);
}
- static void logCreateAssociation(String profile) {
+ /**
+ * Log association creation
+ */
+ public static void logCreateAssociation(String profile) {
write(CDM_ASSOCIATION_ACTION,
CDM_ASSOCIATION_ACTION__ACTION__CREATED,
METRIC_DEVICE_PROFILE.get(profile));
}
- static void logRemoveAssociation(String profile) {
+ /**
+ * Log association removal
+ */
+ public static void logRemoveAssociation(String profile) {
write(CDM_ASSOCIATION_ACTION,
CDM_ASSOCIATION_ACTION__ACTION__REMOVED,
METRIC_DEVICE_PROFILE.get(profile));
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
index 3aae1ec99f55..d38590e0a251 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.os.Binder.getCallingUid;
-import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.TAG;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,13 +41,11 @@ import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.os.Binder;
import android.os.Process;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -58,16 +53,22 @@ import java.util.Map;
import java.util.Set;
/**
- * Utility methods for working with {@link PackageInfo}-s.
+ * Utility methods for working with {@link PackageInfo}.
*/
public final class PackageUtils {
+
+ private static final String TAG = "CDM_PackageUtils";
+
private static final Intent COMPANION_SERVICE_INTENT =
new Intent(CompanionDeviceService.SERVICE_INTERFACE);
private static final String PROPERTY_PRIMARY_TAG =
"android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE";
+ /**
+ * Get package info
+ */
@Nullable
- static PackageInfo getPackageInfo(@NonNull Context context,
+ public static PackageInfo getPackageInfo(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
final PackageManager pm = context.getPackageManager();
final PackageInfoFlags flags = PackageInfoFlags.of(GET_PERMISSIONS | GET_CONFIGURATIONS);
@@ -81,26 +82,32 @@ public final class PackageUtils {
});
}
- static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
+ /**
+ * Require the app to declare the companion device feature.
+ */
+ public static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
// Allow system server to create CDM associations without FEATURE_COMPANION_DEVICE_SETUP
if (getCallingUid() == Process.SYSTEM_UID) {
return;
}
- String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP;
+ PackageInfo packageInfo = getPackageInfo(context, userId, packageName);
+ if (packageInfo == null) {
+ throw new IllegalArgumentException("Package " + packageName + " doesn't exist.");
+ }
- FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures;
+ FeatureInfo[] requestedFeatures = packageInfo.reqFeatures;
if (requestedFeatures != null) {
- for (int i = 0; i < requestedFeatures.length; i++) {
- if (requiredFeature.equals(requestedFeatures[i].name)) {
+ for (FeatureInfo requestedFeature : requestedFeatures) {
+ if (FEATURE_COMPANION_DEVICE_SETUP.equals(requestedFeature.name)) {
return;
}
}
}
throw new IllegalStateException("Must declare uses-feature "
- + requiredFeature
+ + FEATURE_COMPANION_DEVICE_SETUP
+ " in manifest to use this API");
}
@@ -109,7 +116,7 @@ public final class PackageUtils {
* Services marked as "primary" would always appear at the head of the lists, *before*
* all non-primary services.
*/
- static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
+ public static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
@NonNull Context context, @UserIdInt int userId) {
final PackageManager pm = context.getPackageManager();
final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser(
@@ -179,9 +186,7 @@ public final class PackageUtils {
final String[] allowlistedPackages = context.getResources()
.getStringArray(com.android.internal.R.array.config_companionDevicePackages);
if (!ArrayUtils.contains(allowlistedPackages, packageName)) {
- if (DEBUG) {
- Log.d(TAG, packageName + " is not allowlisted.");
- }
+ Slog.d(TAG, packageName + " is not allowlisted.");
return false;
}
@@ -212,13 +217,6 @@ public final class PackageUtils {
if (!requestingPackageSignatureAllowlisted) {
Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName);
- if (DEBUG) {
- Log.d(TAG, " > allowlisted signatures for " + packageName + ": ["
- + String.join(", ", allowlistedSignatureDigestsForRequestingPackage)
- + "]");
- Log.d(TAG, " > actual signatures for " + packageName + ": "
- + Arrays.toString(requestingPackageSignatureDigests));
- }
}
return requestingPackageSignatureAllowlisted;
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index 15bebbae05b1..2cf1f462a7d1 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
@@ -75,16 +75,22 @@ public final class PermissionsUtils {
DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
}
- static void enforcePermissionsForAssociation(@NonNull Context context,
+ /**
+ * Require the app to declare necessary permission for creating association.
+ */
+ public static void enforcePermissionForCreatingAssociation(@NonNull Context context,
@NonNull AssociationRequest request, int packageUid) {
- enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile(), packageUid);
+ enforcePermissionForRequestingProfile(context, request.getDeviceProfile(), packageUid);
if (request.isSelfManaged()) {
- enforceRequestSelfManagedPermission(context, packageUid);
+ enforcePermissionForRequestingSelfManaged(context, packageUid);
}
}
- static void enforceRequestDeviceProfilePermissions(
+ /**
+ * Require the app to declare necessary permission for creating association with profile.
+ */
+ public static void enforcePermissionForRequestingProfile(
@NonNull Context context, @Nullable String deviceProfile, int packageUid) {
// Device profile can be null.
if (deviceProfile == null) return;
@@ -101,7 +107,11 @@ public final class PermissionsUtils {
}
}
- static void enforceRequestSelfManagedPermission(@NonNull Context context, int packageUid) {
+ /**
+ * Require the app to declare necessary permission for creating self-managed association.
+ */
+ public static void enforcePermissionForRequestingSelfManaged(@NonNull Context context,
+ int packageUid) {
if (context.checkPermission(REQUEST_COMPANION_SELF_MANAGED, getCallingPid(), packageUid)
!= PERMISSION_GRANTED) {
throw new SecurityException("Application does not hold "
@@ -109,25 +119,39 @@ public final class PermissionsUtils {
}
}
- static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+ /**
+ * Check if the caller can interact with the user.
+ */
+ public static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) {
if (getCallingUserId() == userId) return true;
return context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED;
}
- static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+ /**
+ * Require the caller to be able to interact with the user.
+ */
+ public static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) {
if (getCallingUserId() == userId) return;
context.enforceCallingPermission(INTERACT_ACROSS_USERS, null);
}
- static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context, int userId) {
+ /**
+ * Require the caller to be system UID or to be able to interact with the user.
+ */
+ public static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context,
+ int userId) {
if (getCallingUid() == SYSTEM_UID) return;
enforceCallerCanInteractWithUserId(context, userId);
}
- static boolean checkCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) {
+ /**
+ * Check if the caller is system UID or the provided user.
+ */
+ public static boolean checkCallerIsSystemOr(@UserIdInt int userId,
+ @NonNull String packageName) {
final int callingUid = getCallingUid();
if (callingUid == SYSTEM_UID) return true;
@@ -158,13 +182,19 @@ public final class PermissionsUtils {
}
}
- static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
+ /**
+ * Check if the caller holds the necessary permission to manage companion devices.
+ */
+ public static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
if (getCallingUid() == SYSTEM_UID) return true;
return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
}
- static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
+ /**
+ * Require the caller to be able to manage the associations for the package.
+ */
+ public static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName,
@Nullable String actionDescription) {
if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return;
@@ -175,7 +205,10 @@ public final class PermissionsUtils {
+ " for u" + userId + "/" + packageName);
}
- static void enforceCallerCanObservingDevicePresenceByUuid(@NonNull Context context) {
+ /**
+ * Require the caller to hold necessary permission to observe device presence by UUID.
+ */
+ public static void enforceCallerCanObservingDevicePresenceByUuid(@NonNull Context context) {
if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE)
!= PERMISSION_GRANTED) {
throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
@@ -193,7 +226,7 @@ public final class PermissionsUtils {
* </ul>
* @return whether the caller is one of the above.
*/
- static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
+ public static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
if (checkCallerIsSystemOr(userId, packageName)) return true;
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/utils/RolesUtils.java
index af9d2d783100..f798e218e8e0 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/RolesUtils.java
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP;
-import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.TAG;
-
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
@@ -29,7 +26,6 @@ import android.companion.AssociationInfo;
import android.content.Context;
import android.os.Binder;
import android.os.UserHandle;
-import android.util.Log;
import android.util.Slog;
import java.util.List;
@@ -37,9 +33,14 @@ import java.util.function.Consumer;
/** Utility methods for accessing {@link RoleManager} APIs. */
@SuppressLint("LongLogTag")
-final class RolesUtils {
+public final class RolesUtils {
+
+ private static final String TAG = "CDM_RolesUtils";
- static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
+ /**
+ * Check if the package holds the role.
+ */
+ public static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
@NonNull String packageName, @NonNull String role) {
final RoleManager roleManager = context.getSystemService(RoleManager.class);
final List<String> roleHolders = roleManager.getRoleHoldersAsUser(
@@ -58,13 +59,9 @@ final class RolesUtils {
* false if failed. If the association does not have any device profile
* specified, then the operation will always be successful as a no-op.
*/
- static void addRoleHolderForAssociation(
+ public static void addRoleHolderForAssociation(
@NonNull Context context, @NonNull AssociationInfo associationInfo,
@NonNull Consumer<Boolean> roleGrantResult) {
- if (DEBUG) {
- Log.d(TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
- }
-
final String deviceProfile = associationInfo.getDeviceProfile();
if (deviceProfile == null) {
// If no device profile is specified, then no-op and resolve callback with success.
@@ -83,33 +80,30 @@ final class RolesUtils {
roleGrantResult);
}
- static void removeRoleHolderForAssociation(
- @NonNull Context context, @NonNull AssociationInfo associationInfo) {
- if (DEBUG) {
- Log.d(TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
- }
-
- final String deviceProfile = associationInfo.getDeviceProfile();
+ /**
+ * Remove the role for the package association.
+ */
+ public static void removeRoleHolderForAssociation(
+ @NonNull Context context, int userId, String packageName, String deviceProfile) {
if (deviceProfile == null) return;
final RoleManager roleManager = context.getSystemService(RoleManager.class);
- final String packageName = associationInfo.getPackageName();
- final int userId = associationInfo.getUserId();
final UserHandle userHandle = UserHandle.of(userId);
- Slog.i(TAG, "Removing CDM role holder, role=" + deviceProfile
- + ", package=u" + userId + "\\" + packageName);
+ Slog.i(TAG, "Removing CDM role=" + deviceProfile
+ + " for userId=" + userId + ", packageName=" + packageName);
final long identity = Binder.clearCallingIdentity();
try {
roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
- MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
- success -> {
- if (!success) {
- Slog.e(TAG, "Failed to remove u" + userId + "\\" + packageName
- + " from the list of " + deviceProfile + " holders.");
- }
- });
+ MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
+ success -> {
+ if (!success) {
+ Slog.e(TAG, "Failed to remove userId=" + userId + ", packageName="
+ + packageName + " from the list of " + deviceProfile
+ + " holders.");
+ }
+ });
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/companion/java/com/android/server/companion/Utils.java b/services/companion/java/com/android/server/companion/utils/Utils.java
index b9f61ecd8c4f..8302997a5705 100644
--- a/services/companion/java/com/android/server/companion/Utils.java
+++ b/services/companion/java/com/android/server/companion/utils/Utils.java
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.ResultReceiver;
@@ -43,5 +45,27 @@ public final class Utils {
return ipcFriendly;
}
+ /**
+ * Return a human-readable string for the BluetoothDevice.
+ */
+ public static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
+ final StringBuilder sb = new StringBuilder(btDevice.getAddress());
+
+ sb.append(" [name=");
+ final String name = btDevice.getName();
+ if (name != null) {
+ sb.append('\'').append(name).append('\'');
+ } else {
+ sb.append("null");
+ }
+
+ final String alias = btDevice.getAlias();
+ if (alias != null) {
+ sb.append(", alias='").append(alias).append("'");
+ }
+
+ return sb.append(']').toString();
+ }
+
private Utils() {}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 6d731b21ac8a..8d93b048e588 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -210,7 +210,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mActivityListener.onTopActivityChanged(displayId, topActivity,
UserHandle.USER_NULL);
} catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener", e);
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
}
}
@@ -220,7 +220,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
try {
mActivityListener.onTopActivityChanged(displayId, topActivity, userId);
} catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener", e);
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
}
}
@@ -229,7 +229,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
try {
mActivityListener.onDisplayEmpty(displayId);
} catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener", e);
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
}
}
};
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index d4ff699c88a7..6b5ba96f6b1b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -50,7 +50,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
-import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyCache;
import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -940,7 +940,7 @@ public class ContentCaptureManagerService extends
return new ContentProtectionConsentManager(
BackgroundThread.getHandler(),
getContext().getContentResolver(),
- LocalServices.getService(DevicePolicyManagerInternal.class));
+ DevicePolicyCache.getInstance());
}
@Nullable
diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
index 488a51a56fee..9aa5d2fce6b8 100644
--- a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
+++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
@@ -16,8 +16,13 @@
package com.android.server.contentprotection;
+import static android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyCache;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -28,6 +33,7 @@ import android.provider.Settings;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
/**
* Manages consent for content protection.
@@ -45,6 +51,8 @@ public class ContentProtectionConsentManager {
@NonNull private final ContentResolver mContentResolver;
+ @NonNull private final DevicePolicyCache mDevicePolicyCache;
+
@NonNull private final DevicePolicyManagerInternal mDevicePolicyManagerInternal;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -53,54 +61,98 @@ public class ContentProtectionConsentManager {
private volatile boolean mCachedPackageVerifierConsent;
- private volatile boolean mCachedContentProtectionConsent;
+ private volatile boolean mCachedContentProtectionUserConsent;
public ContentProtectionConsentManager(
@NonNull Handler handler,
@NonNull ContentResolver contentResolver,
- @NonNull DevicePolicyManagerInternal devicePolicyManagerInternal) {
+ @NonNull DevicePolicyCache devicePolicyCache) {
mContentResolver = contentResolver;
- mDevicePolicyManagerInternal = devicePolicyManagerInternal;
+ mDevicePolicyCache = devicePolicyCache;
+ mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mContentObserver = new SettingsObserver(handler);
- contentResolver.registerContentObserver(
- Settings.Global.getUriFor(KEY_PACKAGE_VERIFIER_USER_CONSENT),
- /* notifyForDescendants= */ false,
- mContentObserver,
- UserHandle.USER_ALL);
-
- mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
- mCachedContentProtectionConsent = isContentProtectionConsentGranted();
+ registerSettingsGlobalObserver(KEY_PACKAGE_VERIFIER_USER_CONSENT);
+ registerSettingsGlobalObserver(KEY_CONTENT_PROTECTION_USER_CONSENT);
+ readPackageVerifierConsentGranted();
+ readContentProtectionUserConsentGranted();
}
- /**
- * Returns true if all the consents are granted
- */
+ /** Returns true if the consent is ultimately granted. */
public boolean isConsentGranted(@UserIdInt int userId) {
- return mCachedPackageVerifierConsent
- && mCachedContentProtectionConsent
- && !isUserOrganizationManaged(userId);
+ return mCachedPackageVerifierConsent && isContentProtectionConsentGranted(userId);
}
+ /**
+ * Not always cached internally and can be expensive, when possible prefer to use {@link
+ * #mCachedPackageVerifierConsent} instead.
+ */
private boolean isPackageVerifierConsentGranted() {
- // Not always cached internally
return Settings.Global.getInt(
mContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, /* def= */ 0)
>= 1;
}
- private boolean isContentProtectionConsentGranted() {
- // Not always cached internally
+ /**
+ * Not always cached internally and can be expensive, when possible prefer to use {@link
+ * #mCachedContentProtectionUserConsent} instead.
+ */
+ private boolean isContentProtectionUserConsentGranted() {
return Settings.Global.getInt(
mContentResolver, KEY_CONTENT_PROTECTION_USER_CONSENT, /* def= */ 0)
>= 0;
}
+ private void readPackageVerifierConsentGranted() {
+ mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+ }
+
+ private void readContentProtectionUserConsentGranted() {
+ mCachedContentProtectionUserConsent = isContentProtectionUserConsentGranted();
+ }
+
+ /** Always cached internally, cheap and safe to use. */
private boolean isUserOrganizationManaged(@UserIdInt int userId) {
- // Cached internally
return mDevicePolicyManagerInternal.isUserOrganizationManaged(userId);
}
+ /** Always cached internally, cheap and safe to use. */
+ private boolean isContentProtectionPolicyGranted(@UserIdInt int userId) {
+ if (!manageDevicePolicyEnabled()) {
+ return false;
+ }
+
+ @DevicePolicyManager.ContentProtectionPolicy
+ int policy = mDevicePolicyCache.getContentProtectionPolicy(userId);
+
+ return switch (policy) {
+ case DevicePolicyManager.CONTENT_PROTECTION_ENABLED -> true;
+ case DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY ->
+ mCachedContentProtectionUserConsent;
+ default -> false;
+ };
+ }
+
+ /** Always cached internally, cheap and safe to use. */
+ private boolean isContentProtectionConsentGranted(@UserIdInt int userId) {
+ if (!manageDevicePolicyEnabled()) {
+ return mCachedContentProtectionUserConsent && !isUserOrganizationManaged(userId);
+ }
+
+ return isUserOrganizationManaged(userId)
+ ? isContentProtectionPolicyGranted(userId)
+ : mCachedContentProtectionUserConsent;
+ }
+
+ private void registerSettingsGlobalObserver(@NonNull String key) {
+ registerSettingsObserver(Settings.Global.getUriFor(key));
+ }
+
+ private void registerSettingsObserver(@NonNull Uri uri) {
+ mContentResolver.registerContentObserver(
+ uri, /* notifyForDescendants= */ false, mContentObserver, UserHandle.USER_ALL);
+ }
+
private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
@@ -108,17 +160,20 @@ public class ContentProtectionConsentManager {
}
@Override
- public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+ public void onChange(boolean selfChange, @Nullable Uri uri, @UserIdInt int userId) {
+ if (uri == null) {
+ return;
+ }
final String property = uri.getLastPathSegment();
if (property == null) {
return;
}
switch (property) {
case KEY_PACKAGE_VERIFIER_USER_CONSENT:
- mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+ readPackageVerifierConsentGranted();
return;
case KEY_CONTENT_PROTECTION_USER_CONSENT:
- mCachedContentProtectionConsent = isContentProtectionConsentGranted();
+ readContentProtectionUserConsentGranted();
return;
default:
Slog.w(TAG, "Ignoring unexpected property: " + property);
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 9189ea763577..1a3ef73ca2a8 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -264,8 +264,8 @@ public class SystemConfig {
final ArrayMap<String, ArraySet<String>> mAllowIgnoreLocationSettings = new ArrayMap<>();
// These are the packages that are allow-listed to be able to access camera when
- // the camera privacy state is for driver assistance apps only.
- final ArrayMap<String, Boolean> mAllowlistCameraPrivacy = new ArrayMap<>();
+ // the camera privacy state is enabled.
+ final ArraySet<String> mAllowlistCameraPrivacy = new ArraySet<>();
// These are the action strings of broadcasts which are whitelisted to
// be delivered anonymously even to apps which target O+.
@@ -348,6 +348,9 @@ public class SystemConfig {
// marked as stopped by the system
@NonNull private final Set<String> mInitialNonStoppedSystemPackages = new ArraySet<>();
+ // Which packages (key) are allowed to join particular SharedUid (value).
+ @NonNull private final Map<String, String> mPackageToSharedUidAllowList = new ArrayMap<>();
+
// A map of preloaded package names and the path to its app metadata file path.
private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>();
@@ -486,7 +489,7 @@ public class SystemConfig {
return mAllowedAssociations;
}
- public ArrayMap<String, Boolean> getCameraPrivacyAllowlist() {
+ public ArraySet<String> getCameraPrivacyAllowlist() {
return mAllowlistCameraPrivacy;
}
@@ -567,6 +570,11 @@ public class SystemConfig {
return mInitialNonStoppedSystemPackages;
}
+ @NonNull
+ public Map<String, String> getPackageToSharedUidAllowList() {
+ return mPackageToSharedUidAllowList;
+ }
+
public ArrayMap<String, String> getAppMetadataFilePaths() {
return mAppMetadataFilePaths;
}
@@ -1068,13 +1076,11 @@ public class SystemConfig {
case "camera-privacy-allowlisted-app" : {
if (allowOverrideAppRestrictions) {
String pkgname = parser.getAttributeValue(null, "package");
- boolean isMandatory = XmlUtils.readBooleanAttribute(
- parser, "mandatory", false);
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
- mAllowlistCameraPrivacy.put(pkgname, isMandatory);
+ mAllowlistCameraPrivacy.add(pkgname);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
@@ -1563,6 +1569,19 @@ public class SystemConfig {
mInitialNonStoppedSystemPackages.add(pkgName);
}
} break;
+ case "allow-package-shareduid": {
+ String pkgName = parser.getAttributeValue(null, "package");
+ String sharedUid = parser.getAttributeValue(null, "shareduid");
+ if (TextUtils.isEmpty(pkgName)) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else if (TextUtils.isEmpty(sharedUid)) {
+ Slog.w(TAG, "<" + name + "> without shareduid in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mPackageToSharedUidAllowList.put(pkgName, sharedUid);
+ }
+ }
case "asl-file": {
String packageName = parser.getAttributeValue(null, "package");
String path = parser.getAttributeValue(null, "path");
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 7dc9f10e646c..4de85fe9f2ff 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -39,7 +39,9 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* The base class for services running in the system process. Override and implement
@@ -135,6 +137,7 @@ public abstract class SystemService {
public @interface BootPhase {}
private final Context mContext;
+ private final List<Class<?>> mDependencies;
/**
* Class representing user in question in the lifecycle callbacks.
@@ -332,7 +335,28 @@ public abstract class SystemService {
* @param context The system server context.
*/
public SystemService(@NonNull Context context) {
+ this(context, Collections.emptyList());
+ }
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ * @param dependencies The list of dependencies that this service requires to operate.
+ * Currently only used by the Ravenwood deviceless testing environment to
+ * understand transitive dependencies needed to support a specific test.
+ * For example, including {@code PowerManager.class} here indicates that
+ * this service requires the {@code PowerManager} and/or {@code
+ * PowerManagerInternal} APIs to function.
+ * @hide
+ */
+ public SystemService(@NonNull Context context, @NonNull List<Class<?>> dependencies) {
mContext = context;
+ mDependencies = Objects.requireNonNull(dependencies);
}
/**
@@ -356,6 +380,22 @@ public abstract class SystemService {
}
/**
+ * Get the list of dependencies that this service requires to operate.
+ *
+ * Currently only used by the Ravenwood deviceless testing environment to understand transitive
+ * dependencies needed to support a specific test.
+ *
+ * For example, including {@code PowerManager.class} here indicates that this service
+ * requires the {@code PowerManager} and/or {@code PowerManagerInternal} APIs to function.
+ *
+ * @hide
+ */
+ @NonNull
+ public final List<Class<?>> getDependencies() {
+ return mDependencies;
+ }
+
+ /**
* Returns true if the system is running in safe mode.
* TODO: we should define in which phase this becomes valid
*
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 5e9d1cbb32ca..8dc15ade532e 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -163,6 +163,9 @@
}
],
"file_patterns": ["PinnerService\\.java"]
+ },
+ {
+ "name": "FrameworksVpnTests"
}
]
}
diff --git a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
index 96fee9296215..0e0bf81253e6 100644
--- a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
+++ b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
@@ -20,6 +20,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM
import android.app.KeyguardManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.AuthenticationStateListener;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -187,6 +188,11 @@ public class AdaptiveAuthService extends SystemService {
}
private void reportAuthAttempt(int authType, boolean success, int userId) {
+ // Disable adaptive auth for automotive devices by default
+ if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ return;
+ }
+
if (success) {
// Deleting the entry effectively resets the counter of failed attempts for the user
mFailedAttemptsForUser.delete(userId);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b8e09cce93b9..258f53d982d2 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -372,6 +372,15 @@ public final class ActiveServices {
@Overridable
public static final long FGS_BOOT_COMPLETED_RESTRICTIONS = 296558535L;
+ /**
+ * Disables foreground service background starts in System Alert Window for all types
+ * unless it already has a System Overlay Window.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM)
+ @Overridable
+ public static final long FGS_SAW_RESTRICTIONS = 319471980L;
+
final ActivityManagerService mAm;
// Maximum number of services that we allow to start in the background
@@ -8526,10 +8535,31 @@ public final class ActiveServices {
}
}
+ // The flag being enabled isn't enough to deny background start: we need to also check
+ // if there is a system alert UI present.
if (ret == REASON_DENIED) {
- if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
- callingPackage)) {
- ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+ // Flag check: are we disabling SAW FGS background starts?
+ final boolean shouldDisableSaw = Flags.fgsDisableSaw()
+ && CompatChanges.isChangeEnabled(FGS_BOOT_COMPLETED_RESTRICTIONS, callingUid);
+ if (shouldDisableSaw) {
+ final ProcessRecord processRecord = mAm
+ .getProcessRecordLocked(targetService.processName,
+ targetService.appInfo.uid);
+ if (processRecord != null) {
+ if (processRecord.mState.hasOverlayUi()) {
+ if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
+ callingPackage)) {
+ ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+ }
+ }
+ } else {
+ Slog.e(TAG, "Could not find process record for SAW check");
+ }
+ } else {
+ if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
+ callingPackage)) {
+ ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e222878a5dd3..cfe1e181871d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4779,36 +4779,47 @@ public class ActivityManagerService extends IActivityManager.Stub
// being bound to an application.
thread.runIsolatedEntryPoint(
app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs());
- } else if (instr2 != null) {
- thread.bindApplication(processName, appInfo,
- app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
- instr2.mIsSdkInSandbox,
- providerList,
- instr2.mClass,
- profilerInfo, instr2.mArguments,
- instr2.mWatcher,
- instr2.mUiAutomationConnection, testMode,
- mBinderTransactionTrackingEnabled, enableTrackAllocation,
- isRestrictedBackupMode || !normalMode, app.isPersistent(),
- new Configuration(app.getWindowProcessController().getConfiguration()),
- app.getCompat(), getCommonServicesLocked(app.isolated),
- mCoreSettingsObserver.getCoreSettingsLocked(),
- buildSerial, autofillOptions, contentCaptureOptions,
- app.getDisabledCompatChanges(), serializedSystemFontMap,
- app.getStartElapsedTime(), app.getStartUptime());
} else {
- thread.bindApplication(processName, appInfo,
- app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
- /* isSdkInSandbox= */ false,
- providerList, null, profilerInfo, null, null, null, testMode,
- mBinderTransactionTrackingEnabled, enableTrackAllocation,
- isRestrictedBackupMode || !normalMode, app.isPersistent(),
+ boolean isSdkInSandbox = false;
+ ComponentName instrumentationName = null;
+ Bundle instrumentationArgs = null;
+ IInstrumentationWatcher instrumentationWatcher = null;
+ IUiAutomationConnection instrumentationUiConnection = null;
+ if (instr2 != null) {
+ isSdkInSandbox = instr2.mIsSdkInSandbox;
+ instrumentationName = instr2.mClass;
+ instrumentationArgs = instr2.mArguments;
+ instrumentationWatcher = instr2.mWatcher;
+ instrumentationUiConnection = instr2.mUiAutomationConnection;
+ }
+ thread.bindApplication(
+ processName,
+ appInfo,
+ app.sdkSandboxClientAppVolumeUuid,
+ app.sdkSandboxClientAppPackage,
+ isSdkInSandbox,
+ providerList,
+ instrumentationName,
+ profilerInfo,
+ instrumentationArgs,
+ instrumentationWatcher,
+ instrumentationUiConnection,
+ testMode,
+ mBinderTransactionTrackingEnabled,
+ enableTrackAllocation,
+ isRestrictedBackupMode || !normalMode,
+ app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
- app.getCompat(), getCommonServicesLocked(app.isolated),
+ app.getCompat(),
+ getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
- buildSerial, autofillOptions, contentCaptureOptions,
- app.getDisabledCompatChanges(), serializedSystemFontMap,
- app.getStartElapsedTime(), app.getStartUptime());
+ buildSerial,
+ autofillOptions,
+ contentCaptureOptions,
+ app.getDisabledCompatChanges(),
+ serializedSystemFontMap,
+ app.getStartElapsedTime(),
+ app.getStartUptime());
}
Message msg = mHandler.obtainMessage(BIND_APPLICATION_TIMEOUT_SOFT_MSG);
@@ -9028,6 +9039,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ Process.myUid());
BinderProxy.dumpProxyDebugInfo();
+ CriticalEventLog.getInstance().logExcessiveBinderCalls(uid);
if (uid == Process.SYSTEM_UID) {
Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
} else {
@@ -9362,7 +9374,9 @@ public class ActivityManagerService extends IActivityManager.Stub
sb.append("Animations-Running: ").append(info.numAnimationsRunning).append("\n");
}
if (info.broadcastIntentAction != null) {
- sb.append("Broadcast-Intent-Action: ").append(info.broadcastIntentAction).append("\n");
+ sb.append("Broadcast-Intent-Action: ")
+ .append(info.broadcastIntentAction)
+ .append("\n");
}
if (info.durationMillis != -1) {
sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
@@ -9722,82 +9736,126 @@ public class ActivityManagerService extends IActivityManager.Stub
// If process is null, we are being called from some internal code
// and may be about to die -- run this synchronously.
final boolean runSynchronously = process == null;
- Thread worker = new Thread("Error dump: " + dropboxTag) {
- @Override
- public void run() {
- if (report != null) {
- sb.append(report);
- }
-
- String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
- String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
- int lines = Build.IS_USER
- ? 0
- : Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
- int dropboxMaxSize = Settings.Global.getInt(
- mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
-
- if (dataFile != null) {
- // Attach the stack traces file to the report so collectors can load them
- // by file if they have access.
- sb.append(DATA_FILE_PATH_HEADER)
- .append(dataFile.getAbsolutePath()).append('\n');
-
- int maxDataFileSize = dropboxMaxSize
- - sb.length()
- - lines * RESERVED_BYTES_PER_LOGCAT_LINE
- - DATA_FILE_PATH_FOOTER.length();
-
- if (maxDataFileSize > 0) {
- // Inline dataFile contents if there is room.
- try {
- sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
- "\n\n[[TRUNCATED]]\n"));
- } catch (IOException e) {
- Slog.e(TAG, "Error reading " + dataFile, e);
+ Thread worker =
+ new Thread("Error dump: " + dropboxTag) {
+ @Override
+ public void run() {
+ if (report != null) {
+ sb.append(report);
}
- }
- // Always append the footer, even there wasn't enough space to inline the
- // dataFile contents.
- sb.append(DATA_FILE_PATH_FOOTER);
- }
+ String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
+ String maxBytesSetting =
+ Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
+ int lines =
+ Build.IS_USER
+ ? 0
+ : Settings.Global.getInt(
+ mContext.getContentResolver(), logcatSetting, 0);
+ int dropboxMaxSize =
+ Settings.Global.getInt(
+ mContext.getContentResolver(),
+ maxBytesSetting,
+ DROPBOX_DEFAULT_MAX_SIZE);
+
+ if (dataFile != null) {
+ // Attach the stack traces file to the report so collectors can load
+ // them
+ // by file if they have access.
+ sb.append(DATA_FILE_PATH_HEADER)
+ .append(dataFile.getAbsolutePath())
+ .append('\n');
+
+ int maxDataFileSize =
+ dropboxMaxSize
+ - sb.length()
+ - lines * RESERVED_BYTES_PER_LOGCAT_LINE
+ - DATA_FILE_PATH_FOOTER.length();
+
+ if (maxDataFileSize > 0) {
+ // Inline dataFile contents if there is room.
+ try {
+ sb.append(
+ FileUtils.readTextFile(
+ dataFile,
+ maxDataFileSize,
+ "\n\n[[TRUNCATED]]\n"));
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + dataFile, e);
+ }
+ }
- if (crashInfo != null && crashInfo.stackTrace != null) {
- sb.append(crashInfo.stackTrace);
- }
+ // Always append the footer, even there wasn't enough space to inline
+ // the
+ // dataFile contents.
+ sb.append(DATA_FILE_PATH_FOOTER);
+ }
- if (lines > 0 && !runSynchronously) {
- sb.append("\n");
+ if (crashInfo != null && crashInfo.stackTrace != null) {
+ sb.append(crashInfo.stackTrace);
+ }
- InputStreamReader input = null;
- try {
- java.lang.Process logcat = new ProcessBuilder(
- // Time out after 10s of inactivity, but kill logcat with SEGV
- // so we can investigate why it didn't finish.
- "/system/bin/timeout", "-i", "-s", "SEGV", "10s",
- // Merge several logcat streams, and take the last N lines.
- "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
- "-b", "main", "-b", "crash", "-t", String.valueOf(lines))
- .redirectErrorStream(true).start();
-
- try { logcat.getOutputStream().close(); } catch (IOException e) {}
- try { logcat.getErrorStream().close(); } catch (IOException e) {}
- input = new InputStreamReader(logcat.getInputStream());
-
- int num;
- char[] buf = new char[8192];
- while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
- } catch (IOException e) {
- Slog.e(TAG, "Error running logcat", e);
- } finally {
- if (input != null) try { input.close(); } catch (IOException e) {}
- }
- }
+ if (lines > 0 && !runSynchronously) {
+ sb.append("\n");
- dbox.addText(dropboxTag, sb.toString());
- }
- };
+ InputStreamReader input = null;
+ try {
+ java.lang.Process logcat =
+ new ProcessBuilder(
+ // Time out after 10s of inactivity, but
+ // kill logcat with SEGV
+ // so we can investigate why it didn't
+ // finish.
+ "/system/bin/timeout",
+ "-i",
+ "-s",
+ "SEGV",
+ "10s",
+ // Merge several logcat streams, and take
+ // the last N lines.
+ "/system/bin/logcat",
+ "-v",
+ "threadtime",
+ "-b",
+ "events",
+ "-b",
+ "system",
+ "-b",
+ "main",
+ "-b",
+ "crash",
+ "-t",
+ String.valueOf(lines))
+ .redirectErrorStream(true)
+ .start();
+
+ try {
+ logcat.getOutputStream().close();
+ } catch (IOException e) {
+ }
+ try {
+ logcat.getErrorStream().close();
+ } catch (IOException e) {
+ }
+ input = new InputStreamReader(logcat.getInputStream());
+
+ int num;
+ char[] buf = new char[8192];
+ while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error running logcat", e);
+ } finally {
+ if (input != null)
+ try {
+ input.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ dbox.addText(dropboxTag, sb.toString());
+ }
+ };
if (runSynchronously) {
final int oldMask = StrictMode.allowThreadDiskWritesMask();
@@ -10165,43 +10223,48 @@ public class ActivityManagerService extends IActivityManager.Stub
mOomAdjuster.dumpCacheOomRankerSettings(pw);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
-
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
dumpAllowedAssociationsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
-
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
if (dumpAll || dumpPackage != null) {
dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
}
mCpHelper.dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
dumpPermissions(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage);
if (!dumpClient) {
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
sdumper.dumpLocked();
}
@@ -10216,7 +10279,8 @@ public class ActivityManagerService extends IActivityManager.Stub
// method with the lock held.
if (dumpClient) {
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
sdumper.dumpWithClient();
}
@@ -10229,33 +10293,38 @@ public class ActivityManagerService extends IActivityManager.Stub
// proxies in the first place.
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */);
}
synchronized(this) {
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mAtmInternal.dump(DUMP_RECENTS_CMD, fd, pw, args, opti, dumpAll, dumpClient,
dumpPackage, displayIdFilter);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mAtmInternal.dump(DUMP_LASTANR_CMD, fd, pw, args, opti, dumpAll, dumpClient,
dumpPackage, displayIdFilter);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mAtmInternal.dump(DUMP_STARTER_CMD, fd, pw, args, opti, dumpAll, dumpClient,
dumpPackage, displayIdFilter);
if (dumpPackage == null) {
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mAtmInternal.dump(DUMP_CONTAINERS_CMD, fd, pw, args, opti, dumpAll, dumpClient,
dumpPackage, displayIdFilter);
@@ -10265,7 +10334,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!dumpNormalPriority) {
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mAtmInternal.dump(DUMP_ACTIVITIES_CMD, fd, pw, args, opti, dumpAll, dumpClient,
dumpPackage, displayIdFilter);
@@ -10273,45 +10343,53 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mAssociations.size() > 0) {
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
mProcessList.getAppStartInfoTracker().dumpHistoryProcessStartInfo(pw, dumpPackage);
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage);
}
if (dumpPackage == null) {
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mOomAdjProfiler.dump(pw);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
dumpLmkLocked(pw);
}
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
synchronized (mProcLock) {
mProcessList.dumpProcessesLSP(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
}
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
dumpUsers(pw);
pw.println();
if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
}
mComponentAliasResolver.dump(pw);
}
@@ -10321,7 +10399,8 @@ public class ActivityManagerService extends IActivityManager.Stub
* Dump the app restriction controller, it's required not to hold the global lock here.
*/
private void dumpAppRestrictionController(PrintWriter pw) {
- pw.println("-------------------------------------------------------------------------------");
+ pw.println(
+ "-------------------------------------------------------------------------------");
mAppRestrictionController.dump(pw, "");
}
@@ -11461,7 +11540,9 @@ public class ActivityManagerService extends IActivityManager.Stub
void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
- pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
+ pw.println(
+ "ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity"
+ + " allowed-associations)");
boolean printed = false;
if (mAllowedAssociations != null) {
for (int i = 0; i < mAllowedAssociations.size(); i++) {
@@ -12815,7 +12896,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!opts.isCompact) {
pw.print(" Used RAM: "); pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss
+ kernelUsed)); pw.print(" (");
- pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss)); pw.print(" used pss + ");
+ pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss));
+ pw.print(" used pss + ");
pw.print(stringifyKBSize(kernelUsed)); pw.print(" kernel)\n");
pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM));
} else {
@@ -13685,8 +13767,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
validateServiceInstanceName(instanceName);
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
- "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
+ if (DEBUG_SERVICE)
+ Slog.v(
+ TAG_SERVICE,
+ "*** startService: "
+ + service
+ + " type="
+ + resolvedType
+ + " fg="
+ + requireForeground);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
@@ -15447,9 +15536,14 @@ public class ActivityManagerService extends IActivityManager.Stub
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
- + callingPid + ", uid=" + callingUid
- + " requires " + android.Manifest.permission.BROADCAST_STICKY;
+ String msg =
+ "Permission Denial: broadcastIntent() requesting a sticky broadcast from"
+ + " pid="
+ + callingPid
+ + ", uid="
+ + callingUid
+ + " requires "
+ + android.Manifest.permission.BROADCAST_STICKY;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index d1c8c303524b..4a3791396828 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -276,6 +276,11 @@ class BroadcastProcessQueue {
&& record.getDeliveryGroupPolicy() == BroadcastOptions.DELIVERY_GROUP_POLICY_ALL) {
final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex);
if (replacedBroadcastRecord != null) {
+ if (mLastDeferredStates && shouldBeDeferred()
+ && (record.getDeliveryState(recordIndex)
+ == BroadcastRecord.DELIVERY_PENDING)) {
+ deferredStatesApplyConsumer.accept(record, recordIndex);
+ }
return replacedBroadcastRecord;
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 34ba7f0debb0..3abfe082db27 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1468,7 +1468,7 @@ class UserController implements Handler.Callback {
// Send PROFILE_INACCESSIBLE broadcast if a profile was stopped
final UserInfo userInfo = getUserInfo(userId);
- if (userInfo.isProfile()) {
+ if (userInfo != null && userInfo.isProfile()) {
UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null) {
broadcastProfileAccessibleStateChanged(userId, parent.id,
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 16dbe18f1555..b1823b4ee38f 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -23,6 +23,13 @@ flag {
}
flag {
+ name: "fgs_disable_saw"
+ namespace: "backstage_power"
+ description: "Disable System Alert Window FGS start"
+ bug: "296558535"
+}
+
+flag {
name: "bfgs_managed_network_access"
namespace: "backstage_power"
description: "Restrict network access for certain applications in BFGS process state"
@@ -53,3 +60,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "backstage_power"
+ name: "defer_outgoing_bcasts"
+ description: "Defer outgoing broadcasts from processes in freezable state"
+ bug: "327496592"
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 19dd7b7ea2f6..d4f04b5ad760 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3713,7 +3713,7 @@ public class AudioService extends IAudioService.Stub
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume: postSetAvrcpAbsoluteVolumeIndex index="
+ Log.d(TAG, "adjustStreamVolume: postSetAvrcpAbsoluteVolumeIndex index="
+ newIndex + "stream=" + streamType);
}
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
@@ -3727,7 +3727,7 @@ public class AudioService extends IAudioService.Stub
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ Log.d(TAG, "adjustStreamVolume postSetLeAudioVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
mDeviceBroker.postSetLeAudioVolumeIndex(newIndex,
@@ -3740,7 +3740,7 @@ public class AudioService extends IAudioService.Stub
// the one expected by the hearing aid
if (streamType == getBluetoothContextualVolumeStream()) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
+ Log.d(TAG, "adjustStreamVolume postSetHearingAidVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
@@ -4722,7 +4722,7 @@ public class AudioService extends IAudioService.Stub
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ Log.d(TAG, "setStreamVolume postSetLeAudioVolumeIndex index="
+ index + " stream=" + streamType);
}
mDeviceBroker.postSetLeAudioVolumeIndex(index, mStreamStates[streamType].getMaxIndex(),
diff --git a/services/core/java/com/android/server/biometrics/log/ALSProbe.java b/services/core/java/com/android/server/biometrics/log/ALSProbe.java
index d584c99cea72..d4e46a930124 100644
--- a/services/core/java/com/android/server/biometrics/log/ALSProbe.java
+++ b/services/core/java/com/android/server/biometrics/log/ALSProbe.java
@@ -179,15 +179,18 @@ final class ALSProbe implements Probe {
nextConsumer.consume(current);
} else if (mNextConsumer != null) {
mNextConsumer.add(nextConsumer);
- } else {
+ } else if (mLightSensor != null) {
mDestroyed = false;
mNextConsumer = nextConsumer;
enableLightSensorLoggingLocked();
+ } else {
+ Slog.w(TAG, "No light sensor - use current to consume");
+ nextConsumer.consume(current);
}
}
private void enableLightSensorLoggingLocked() {
- if (!mEnabled) {
+ if (!mEnabled && mLightSensor != null) {
mEnabled = true;
mLastAmbientLux = -1;
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
@@ -201,7 +204,7 @@ final class ALSProbe implements Probe {
private void disableLightSensorLoggingLocked(boolean destroying) {
resetTimerLocked(false /* start */);
- if (mEnabled) {
+ if (mEnabled && mLightSensor != null) {
mEnabled = false;
if (!destroying) {
mLastAmbientLux = -1;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 007b7462f637..fb826c824354 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -24,6 +24,7 @@ import android.app.SynchronousUserSwitchObserver;
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
@@ -39,6 +40,7 @@ import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -64,6 +66,7 @@ import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -359,6 +362,17 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
null /* callback */);
}
+ if (Build.isDebuggable()) {
+ BiometricUtils<Face> utils = FaceUtils.getInstance(
+ mFaceSensors.keyAt(0));
+ for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+ List<Face> enrollments = utils.getBiometricsForUser(mContext, user.id);
+ Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+ + enrollments.stream().map(
+ BiometricAuthenticator.Identifier::getBiometricId).toList());
+ }
+ }
+
return mDaemon;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index a104cf4e1726..c04c47e2d95a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -27,6 +27,7 @@ import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
@@ -46,6 +47,7 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -71,6 +73,7 @@ import com.android.server.biometrics.sensors.AuthenticationStateListeners;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -382,6 +385,17 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
null /* callback */);
}
+ if (Build.isDebuggable()) {
+ BiometricUtils<Fingerprint> utils = FingerprintUtils.getInstance(
+ mFingerprintSensors.keyAt(0));
+ for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+ List<Fingerprint> enrollments = utils.getBiometricsForUser(mContext, user.id);
+ Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+ + enrollments.stream().map(
+ BiometricAuthenticator.Identifier::getBiometricId).toList());
+ }
+ }
+
return mDaemon;
}
diff --git a/services/core/java/com/android/server/connectivity/TEST_MAPPING b/services/core/java/com/android/server/connectivity/TEST_MAPPING
index 687d4b06b4c0..f50831964303 100644
--- a/services/core/java/com/android/server/connectivity/TEST_MAPPING
+++ b/services/core/java/com/android/server/connectivity/TEST_MAPPING
@@ -7,7 +7,7 @@
"exclude-annotation": "com.android.testutils.SkipPresubmit"
}
],
- "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
+ "file_patterns": ["VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
}
],
"presubmit-large": [
@@ -26,5 +26,10 @@
],
"file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
}
+ ],
+ "postsubmit":[
+ {
+ "name":"FrameworksVpnTests"
+ }
]
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 816c3490d0a0..036284f53749 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -30,6 +30,7 @@ import com.android.server.criticalevents.nano.CriticalEventProto;
import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
import com.android.server.criticalevents.nano.CriticalEventProto.InstallPackages;
+import com.android.server.criticalevents.nano.CriticalEventProto.ExcessiveBinderCalls;
import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.SystemServerStarted;
@@ -143,6 +144,15 @@ public class CriticalEventLog {
return System.currentTimeMillis();
}
+ /** Logs when a uid sends an excessive number of binder calls. */
+ public void logExcessiveBinderCalls(int uid) {
+ CriticalEventProto event = new CriticalEventProto();
+ ExcessiveBinderCalls calls = new ExcessiveBinderCalls();
+ calls.uid = uid;
+ event.setExcessiveBinderCalls(calls);
+ log(event);
+ }
+
/** Logs when one or more packages are installed. */
public void logInstallPackagesStarted() {
CriticalEventProto event = new CriticalEventProto();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index fb4976d3cef2..b2a738fd352b 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -463,12 +463,6 @@ public class AutomaticBrightnessController {
boolean userChangedAutoBrightnessAdjustment, int displayPolicy,
boolean shouldResetShortTermModel) {
mState = state;
- // While dozing, the application processor may be suspended which will prevent us from
- // receiving new information from the light sensor. On some devices, we may be able to
- // switch to a wake-up light sensor instead but for now we will simply disable the sensor
- // and hold onto the last computed screen auto brightness. We save the dozing flag for
- // debugging purposes.
- boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
boolean changed = setBrightnessConfiguration(configuration, shouldResetShortTermModel);
changed |= setDisplayPolicy(displayPolicy);
if (userChangedAutoBrightnessAdjustment) {
@@ -482,10 +476,10 @@ public class AutomaticBrightnessController {
}
final boolean userInitiatedChange =
userChangedBrightness || userChangedAutoBrightnessAdjustment;
- if (userInitiatedChange && enable && !dozing) {
+ if (userInitiatedChange && enable) {
prepareBrightnessAdjustmentSample();
}
- changed |= setLightSensorEnabled(enable && !dozing);
+ changed |= setLightSensorEnabled(enable);
if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) {
// Maximum brightness has changed, so recalculate display brightness.
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index a43f93a9beac..91e560e19f0d 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -20,7 +20,6 @@ import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIG
import android.annotation.Nullable;
import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManager;
import android.os.Trace;
/**
@@ -110,7 +109,6 @@ public class DisplayOffloadSessionImpl implements DisplayManagerInternal.Display
try {
mDisplayOffloader.stopOffload();
mIsActive = false;
- mDisplayPowerController.setBrightnessFromOffload(PowerManager.BRIGHTNESS_INVALID_FLOAT);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 23ca81468294..7f014f6dae28 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -375,7 +375,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// information.
// At the time of this writing, this value is changed within updatePowerState() only, which is
// limited to the thread used by DisplayControllerHandler.
- private final BrightnessReason mBrightnessReason = new BrightnessReason();
+ @VisibleForTesting
+ final BrightnessReason mBrightnessReason = new BrightnessReason();
private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
// Brightness animation ramp rates in brightness units per second
@@ -1379,7 +1380,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Switch to doze auto-brightness mode if needed
if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
&& !mAutomaticBrightnessController.isInIdleMode()) {
- mAutomaticBrightnessController.switchMode(mPowerRequest.policy == POLICY_DOZE
+ mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
}
@@ -1434,7 +1435,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness();
// Apply auto-brightness.
int brightnessAdjustmentFlags = 0;
- if (Float.isNaN(brightnessState)) {
+ // AutomaticBrightnessStrategy has higher priority than OffloadBrightnessStrategy
+ if (Float.isNaN(brightnessState)
+ || mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_OFFLOAD) {
if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) {
brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
mTempBrightnessEvent);
@@ -1455,8 +1458,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mScreenOffBrightnessSensorController != null) {
mScreenOffBrightnessSensorController.setLightSensorEnabled(false);
}
+ setBrightnessFromOffload(PowerManager.BRIGHTNESS_INVALID_FLOAT);
} else {
mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
+ // Restore the lower-priority brightness strategy
+ brightnessState = displayBrightnessState.getBrightness();
}
}
} else {
@@ -1485,8 +1491,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
// Use default brightness when dozing unless overridden.
- if ((Float.isNaN(brightnessState))
- && Display.isDozeState(state)) {
+ if (Float.isNaN(brightnessState) && mPowerRequest.policy == POLICY_DOZE) {
rawBrightnessState = mScreenBrightnessDozeConfig;
brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
@@ -1725,6 +1730,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mTempBrightnessEvent.setBrightness(brightnessState);
mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
mTempBrightnessEvent.setDisplayState(state);
+ mTempBrightnessEvent.setDisplayPolicy(mPowerRequest.policy);
mTempBrightnessEvent.setReason(mBrightnessReason);
mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax());
mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode());
@@ -3020,9 +3026,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
setDwbcLoggingEnabled(msg.arg1);
break;
case MSG_SET_BRIGHTNESS_FROM_OFFLOAD:
- mDisplayBrightnessController.setBrightnessFromOffload(
- Float.intBitsToFloat(msg.arg1));
- updatePowerState();
+ if (mDisplayBrightnessController.setBrightnessFromOffload(
+ Float.intBitsToFloat(msg.arg1))) {
+ updatePowerState();
+ }
break;
}
}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 5423b74711a2..82b401a7cc83 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -16,6 +16,9 @@
package com.android.server.display.brightness;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString;
+
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
@@ -46,6 +49,7 @@ public final class BrightnessEvent {
private int mDisplayId;
private String mPhysicalDisplayId;
private int mDisplayState;
+ private int mDisplayPolicy;
private long mTime;
private float mLux;
private float mPreThresholdLux;
@@ -85,6 +89,7 @@ public final class BrightnessEvent {
mDisplayId = that.getDisplayId();
mPhysicalDisplayId = that.getPhysicalDisplayId();
mDisplayState = that.mDisplayState;
+ mDisplayPolicy = that.mDisplayPolicy;
mTime = that.getTime();
// Lux values
mLux = that.getLux();
@@ -117,6 +122,7 @@ public final class BrightnessEvent {
mTime = SystemClock.uptimeMillis();
mPhysicalDisplayId = "";
mDisplayState = Display.STATE_UNKNOWN;
+ mDisplayPolicy = POLICY_OFF;
// Lux values
mLux = 0;
mPreThresholdLux = 0;
@@ -155,6 +161,7 @@ public final class BrightnessEvent {
&& mDisplayId == that.mDisplayId
&& mPhysicalDisplayId.equals(that.mPhysicalDisplayId)
&& mDisplayState == that.mDisplayState
+ && mDisplayPolicy == that.mDisplayPolicy
&& Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux)
&& Float.floatToRawIntBits(mPreThresholdLux)
== Float.floatToRawIntBits(that.mPreThresholdLux)
@@ -191,6 +198,7 @@ public final class BrightnessEvent {
+ "disp=" + mDisplayId
+ ", physDisp=" + mPhysicalDisplayId
+ ", displayState=" + Display.stateToString(mDisplayState)
+ + ", displayPolicy=" + policyToString(mDisplayPolicy)
+ ", brt=" + mBrightness + ((mFlags & FLAG_USER_SET) != 0 ? "(user_set)" : "")
+ ", initBrt=" + mInitialBrightness
+ ", rcmdBrt=" + mRecommendedBrightness
@@ -251,6 +259,10 @@ public final class BrightnessEvent {
mDisplayState = state;
}
+ public void setDisplayPolicy(int policy) {
+ mDisplayPolicy = policy;
+ }
+
public float getLux() {
return mLux;
}
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index f6d02dbc46df..34d53be6933a 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -26,6 +26,7 @@ import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.AutomaticBrightnessController;
import com.android.server.display.BrightnessMappingStrategy;
import com.android.server.display.BrightnessSetting;
@@ -175,14 +176,19 @@ public final class DisplayBrightnessController {
/**
* Sets the brightness from the offload session.
+ * @return Whether the offload brightness has changed
*/
- public void setBrightnessFromOffload(float brightness) {
+ public boolean setBrightnessFromOffload(float brightness) {
synchronized (mLock) {
- if (mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy() != null) {
+ if (mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy() != null
+ && !BrightnessSynchronizer.floatEquals(mDisplayBrightnessStrategySelector
+ .getOffloadBrightnessStrategy().getOffloadScreenBrightness(), brightness)) {
mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()
.setOffloadScreenBrightness(brightness);
+ return true;
}
}
+ return false;
}
/**
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index babc36efcca8..71cc8725f85b 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -133,7 +133,8 @@ public class DisplayBrightnessStrategySelector {
} else if (BrightnessUtils.isValidBrightnessValue(
mTemporaryBrightnessStrategy.getTemporaryScreenBrightness())) {
displayBrightnessStrategy = mTemporaryBrightnessStrategy;
- } else if (mOffloadBrightnessStrategy != null && BrightnessUtils.isValidBrightnessValue(
+ } else if (mAutomaticBrightnessStrategy.shouldUseAutoBrightness()
+ && mOffloadBrightnessStrategy != null && BrightnessUtils.isValidBrightnessValue(
mOffloadBrightnessStrategy.getOffloadScreenBrightness())) {
displayBrightnessStrategy = mOffloadBrightnessStrategy;
}
@@ -200,12 +201,9 @@ public class DisplayBrightnessStrategySelector {
// We are not checking the targetDisplayState, but rather relying on the policy because
// a user can define a different display state(displayPowerRequest.dozeScreenState) too
// in the request with the Doze policy
- if (displayPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE) {
- if (!mAllowAutoBrightnessWhileDozingConfig) {
- return true;
- }
- }
- return false;
+ return displayPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE
+ && !mAllowAutoBrightnessWhileDozingConfig
+ && BrightnessUtils.isValidBrightnessValue(displayPowerRequest.dozeScreenBrightness);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 8eaecef6e562..8b54b22082fd 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -15,6 +15,8 @@
*/
package com.android.server.display.brightness.strategy;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
@@ -102,12 +104,10 @@ public class AutomaticBrightnessStrategy {
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
final boolean autoBrightnessEnabledInDoze =
- allowAutoBrightnessWhileDozingConfig
- && Display.isDozeState(targetDisplayState);
+ allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE;
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
&& (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
- && brightnessReason != BrightnessReason.REASON_OFFLOAD
&& mAutomaticBrightnessController != null;
mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness()
&& !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze);
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
new file mode 100644
index 000000000000..10b5eff06e0c
--- /dev/null
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.flags"
+
+flag {
+ namespace: "wear_frameworks"
+ name: "enable_odp_feature_guard"
+ description: "Enable guard based on system feature to prevent OnDevicePersonalization service from starting on form factors."
+ bug: "322249125"
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index a79e7715f064..05b1cb69235b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -54,6 +54,7 @@ import android.hardware.input.InputManager;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.InputSettings;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult;
import android.hardware.input.TouchCalibration;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
@@ -1244,9 +1245,9 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public String getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
- @Nullable InputMethodSubtype imeSubtype) {
+ public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ InputDeviceIdentifier identifier, @UserIdInt int userId,
+ @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
return mKeyboardLayoutManager.getKeyboardLayoutForInputDevice(identifier, userId,
imeInfo, imeSubtype);
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 46668de042d4..283e692ffbab 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -16,10 +16,11 @@
package com.android.server.input;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+import static android.hardware.input.KeyboardLayoutSelectionResult.FAILED;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT;
import android.annotation.AnyThread;
import android.annotation.MainThread;
@@ -46,6 +47,7 @@ import android.content.res.XmlResourceParser;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult;
import android.icu.lang.UScript;
import android.icu.util.ULocale;
import android.os.Bundle;
@@ -79,7 +81,6 @@ import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.input.KeyboardMetricsCollector.KeyboardConfigurationEvent;
-import com.android.server.input.KeyboardMetricsCollector.LayoutSelectionCriteria;
import com.android.server.inputmethod.InputMethodManagerInternal;
import libcore.io.Streams;
@@ -130,7 +131,8 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
// This cache stores "best-matched" layouts so that we don't need to run the matching
// algorithm repeatedly.
@GuardedBy("mKeyboardLayoutCache")
- private final Map<String, KeyboardLayoutInfo> mKeyboardLayoutCache = new ArrayMap<>();
+ private final Map<String, KeyboardLayoutSelectionResult> mKeyboardLayoutCache =
+ new ArrayMap<>();
private final Object mImeInfoLock = new Object();
@Nullable
@GuardedBy("mImeInfoLock")
@@ -222,17 +224,17 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
} else {
Set<String> selectedLayouts = new HashSet<>();
List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
- List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>();
+ List<KeyboardLayoutSelectionResult> resultList = new ArrayList<>();
boolean hasMissingLayout = false;
for (ImeInfo imeInfo : imeInfoList) {
// Check if the layout has been previously configured
- KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
keyboardIdentifier, imeInfo);
- boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
+ boolean noLayoutFound = result.getLayoutDescriptor() == null;
if (!noLayoutFound) {
- selectedLayouts.add(layoutInfo.mDescriptor);
+ selectedLayouts.add(result.getLayoutDescriptor());
}
- layoutInfoList.add(layoutInfo);
+ resultList.add(result);
hasMissingLayout |= noLayoutFound;
}
@@ -260,7 +262,7 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
if (shouldLogConfiguration) {
- logKeyboardConfigurationEvent(inputDevice, imeInfoList, layoutInfoList,
+ logKeyboardConfigurationEvent(inputDevice, imeInfoList, resultList,
!mDataStore.hasInputDeviceEntry(key));
}
} finally {
@@ -757,10 +759,10 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
String keyboardLayoutDescriptor;
if (useNewSettingsUi()) {
synchronized (mImeInfoLock) {
- KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
new KeyboardIdentifier(identifier, languageTag, layoutType),
mCurrentImeInfo);
- keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor;
+ keyboardLayoutDescriptor = result.getLayoutDescriptor();
}
} else {
keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
@@ -788,26 +790,26 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
@AnyThread
- @Nullable
- public String getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
- @Nullable InputMethodSubtype imeSubtype) {
+ @NonNull
+ public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ InputDeviceIdentifier identifier, @UserIdInt int userId,
+ @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
if (!useNewSettingsUi()) {
Slog.e(TAG, "getKeyboardLayoutForInputDevice() API not supported");
- return null;
+ return FAILED;
}
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
- return null;
+ return FAILED;
}
KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
- KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
keyboardIdentifier, new ImeInfo(userId, imeInfo, imeSubtype));
if (DEBUG) {
Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : "
- + userId + ", subtype = " + imeSubtype + " -> " + layoutInfo);
+ + userId + ", subtype = " + imeSubtype + " -> " + result);
}
- return layoutInfo != null ? layoutInfo.mDescriptor : null;
+ return result;
}
@AnyThread
@@ -942,13 +944,13 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
@Nullable
- private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal(
+ private KeyboardLayoutSelectionResult getKeyboardLayoutForInputDeviceInternal(
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) {
String layoutKey = new LayoutKey(keyboardIdentifier, imeInfo).toString();
synchronized (mDataStore) {
String layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(), layoutKey);
if (layout != null) {
- return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER);
+ return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_USER);
}
}
@@ -961,17 +963,17 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
keyboardIdentifier, imeInfo);
// Call auto-matching algorithm to find the best matching layout
- KeyboardLayoutInfo layoutInfo =
+ KeyboardLayoutSelectionResult result =
getDefaultKeyboardLayoutBasedOnImeInfo(keyboardIdentifier, imeInfo,
layoutList);
- mKeyboardLayoutCache.put(layoutKey, layoutInfo);
- return layoutInfo;
+ mKeyboardLayoutCache.put(layoutKey, result);
+ return result;
}
}
}
- @Nullable
- private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo(
+ @NonNull
+ private static KeyboardLayoutSelectionResult getDefaultKeyboardLayoutBasedOnImeInfo(
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo,
KeyboardLayout[] layoutList) {
Arrays.sort(layoutList);
@@ -986,7 +988,7 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+ "vendor and product Ids. " + keyboardIdentifier
+ " : " + layout.getDescriptor());
}
- return new KeyboardLayoutInfo(layout.getDescriptor(),
+ return new KeyboardLayoutSelectionResult(layout.getDescriptor(),
LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -1004,13 +1006,14 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+ "HW information (Language tag and Layout type). "
+ keyboardIdentifier + " : " + layoutDesc);
}
- return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE);
+ return new KeyboardLayoutSelectionResult(layoutDesc,
+ LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
if (imeInfo == null || imeInfo.mImeSubtypeHandle == null || imeInfo.mImeSubtype == null) {
// Can't auto select layout based on IME info is null
- return null;
+ return FAILED;
}
InputMethodSubtype subtype = imeInfo.mImeSubtype;
@@ -1027,9 +1030,10 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+ layoutDesc);
}
if (layoutDesc != null) {
- return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD);
+ return new KeyboardLayoutSelectionResult(layoutDesc,
+ LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD);
}
- return null;
+ return FAILED;
}
@Nullable
@@ -1246,21 +1250,23 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
private void logKeyboardConfigurationEvent(@NonNull InputDevice inputDevice,
- @NonNull List<ImeInfo> imeInfoList, @NonNull List<KeyboardLayoutInfo> layoutInfoList,
+ @NonNull List<ImeInfo> imeInfoList,
+ @NonNull List<KeyboardLayoutSelectionResult> resultList,
boolean isFirstConfiguration) {
- if (imeInfoList.isEmpty() || layoutInfoList.isEmpty()) {
+ if (imeInfoList.isEmpty() || resultList.isEmpty()) {
return;
}
KeyboardConfigurationEvent.Builder configurationEventBuilder =
new KeyboardConfigurationEvent.Builder(inputDevice).setIsFirstTimeConfiguration(
isFirstConfiguration);
for (int i = 0; i < imeInfoList.size(); i++) {
- KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i);
+ KeyboardLayoutSelectionResult result = resultList.get(i);
String layoutName = null;
int layoutSelectionCriteria = LAYOUT_SELECTION_CRITERIA_DEFAULT;
- if (layoutInfo != null && layoutInfo.mDescriptor != null) {
- layoutSelectionCriteria = layoutInfo.mSelectionCriteria;
- KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(layoutInfo.mDescriptor);
+ if (result != null && result.getLayoutDescriptor() != null) {
+ layoutSelectionCriteria = result.getSelectionCriteria();
+ KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(
+ result.getLayoutDescriptor());
if (d != null) {
layoutName = d.keyboardLayoutName;
}
@@ -1477,33 +1483,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
}
- private static class KeyboardLayoutInfo {
- @Nullable
- private final String mDescriptor;
- @LayoutSelectionCriteria
- private final int mSelectionCriteria;
-
- private KeyboardLayoutInfo(@Nullable String descriptor,
- @LayoutSelectionCriteria int selectionCriteria) {
- mDescriptor = descriptor;
- mSelectionCriteria = selectionCriteria;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof KeyboardLayoutInfo) {
- return Objects.equals(mDescriptor, ((KeyboardLayoutInfo) obj).mDescriptor)
- && mSelectionCriteria == ((KeyboardLayoutInfo) obj).mSelectionCriteria;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return 31 * mSelectionCriteria + mDescriptor.hashCode();
- }
- }
-
private interface KeyboardLayoutVisitor {
void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout);
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index 277a4d40c7e4..b8ae737919d9 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -16,13 +16,18 @@
package com.android.server.input;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT;
+import static android.hardware.input.KeyboardLayoutSelectionResult.layoutSelectionCriteriaToString;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.role.RoleManager;
import android.content.Intent;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria;
import android.icu.util.ULocale;
import android.text.TextUtils;
import android.util.Log;
@@ -37,8 +42,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KeyboardConfiguredProto.KeyboardLayoutConfig;
import com.android.internal.os.KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.policy.ModifierShortcutManager;
-import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -55,32 +60,6 @@ public final class KeyboardMetricsCollector {
// (requires restart)
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- @Retention(SOURCE)
- @IntDef(prefix = {"LAYOUT_SELECTION_CRITERIA_"}, value = {
- LAYOUT_SELECTION_CRITERIA_UNSPECIFIED,
- LAYOUT_SELECTION_CRITERIA_USER,
- LAYOUT_SELECTION_CRITERIA_DEVICE,
- LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
- LAYOUT_SELECTION_CRITERIA_DEFAULT
- })
- public @interface LayoutSelectionCriteria {
- }
-
- /** Unspecified layout selection criteria */
- public static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED = 0;
-
- /** Manual selection by user */
- public static final int LAYOUT_SELECTION_CRITERIA_USER = 1;
-
- /** Auto-detection based on device provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 2;
-
- /** Auto-detection based on IME provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 3;
-
- /** Default selection */
- public static final int LAYOUT_SELECTION_CRITERIA_DEFAULT = 4;
-
@VisibleForTesting
static final String DEFAULT_LAYOUT_NAME = "Default";
@@ -318,6 +297,13 @@ public final class KeyboardMetricsCollector {
}
}
+ // The shortcut may be targeting a system role rather than using an intent selector,
+ // so check for that.
+ String role = intent.getStringExtra(ModifierShortcutManager.EXTRA_ROLE);
+ if (!TextUtils.isEmpty(role)) {
+ return getLogEventFromRole(role);
+ }
+
Set<String> intentCategories = intent.getCategories();
if (intentCategories == null || intentCategories.isEmpty()
|| !intentCategories.contains(Intent.CATEGORY_LAUNCHER)) {
@@ -363,6 +349,23 @@ public final class KeyboardMetricsCollector {
return null;
}
}
+
+ /**
+ * Find KeyboardLogEvent corresponding to the provide system role name.
+ * Returns {@code null} if no matching event found.
+ */
+ @Nullable
+ private static KeyboardLogEvent getLogEventFromRole(String role) {
+ if (RoleManager.ROLE_BROWSER.equals(role)) {
+ return LAUNCH_DEFAULT_BROWSER;
+ } else if (RoleManager.ROLE_SMS.equals(role)) {
+ return LAUNCH_DEFAULT_MESSAGING;
+ } else {
+ Log.w(TAG, "Keyboard shortcut to launch "
+ + role + " not supported for logging");
+ return null;
+ }
+ }
}
/**
@@ -603,30 +606,16 @@ public final class KeyboardMetricsCollector {
@Override
public String toString() {
- return "{keyboardLanguageTag = " + keyboardLanguageTag + " keyboardLayoutType = "
+ return "{keyboardLanguageTag = " + keyboardLanguageTag
+ + " keyboardLayoutType = "
+ KeyboardLayout.LayoutType.getLayoutNameFromValue(keyboardLayoutType)
- + " keyboardLayoutName = " + keyboardLayoutName + " layoutSelectionCriteria = "
- + getStringForSelectionCriteria(layoutSelectionCriteria)
- + "imeLanguageTag = " + imeLanguageTag + " imeLayoutType = "
- + KeyboardLayout.LayoutType.getLayoutNameFromValue(imeLayoutType) + "}";
- }
- }
-
- private static String getStringForSelectionCriteria(
- @LayoutSelectionCriteria int layoutSelectionCriteria) {
- switch (layoutSelectionCriteria) {
- case LAYOUT_SELECTION_CRITERIA_UNSPECIFIED:
- return "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED";
- case LAYOUT_SELECTION_CRITERIA_USER:
- return "LAYOUT_SELECTION_CRITERIA_USER";
- case LAYOUT_SELECTION_CRITERIA_DEVICE:
- return "LAYOUT_SELECTION_CRITERIA_DEVICE";
- case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD:
- return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD";
- case LAYOUT_SELECTION_CRITERIA_DEFAULT:
- return "LAYOUT_SELECTION_CRITERIA_DEFAULT";
- default:
- return "INVALID_CRITERIA";
+ + " keyboardLayoutName = " + keyboardLayoutName
+ + " layoutSelectionCriteria = "
+ + layoutSelectionCriteriaToString(layoutSelectionCriteria)
+ + " imeLanguageTag = " + imeLanguageTag
+ + " imeLayoutType = " + KeyboardLayout.LayoutType.getLayoutNameFromValue(
+ imeLayoutType)
+ + "}";
}
}
diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
index b30f5ecb2dd5..6eae9a4bbe22 100644
--- a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -229,7 +229,8 @@ public class FocusEventDebugView extends RelativeLayout {
/** Report a key event to the debug view. */
@AnyThread
public void reportKeyEvent(KeyEvent event) {
- post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
+ KeyEvent keyEvent = KeyEvent.obtain(event);
+ post(() -> handleKeyEvent(keyEvent));
}
/** Report a motion event to the debug view. */
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index f1698dd0f025..73f1aad31d72 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.ResultReceiver;
import android.util.EventLog;
import android.util.Slog;
+import android.view.MotionEvent;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
@@ -75,7 +76,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ public void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
@@ -88,11 +89,12 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) {
if (DEBUG_IME_VISIBILITY) {
- EventLog.writeEvent(IMF_SHOW_IME, statsToken.getTag(),
- Objects.toString(mService.mCurFocusedWindow),
+ EventLog.writeEvent(IMF_SHOW_IME,
+ statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE,
+ Objects.toString(mService.mImeBindingState.mFocusedWindow),
InputMethodDebug.softInputDisplayReasonToString(reason),
InputMethodDebug.softInputModeToString(
- mService.mCurFocusedWindowSoftInputMode));
+ mService.mImeBindingState.mFocusedWindowSoftInputMode));
}
mService.onShowHideSoftInputRequested(true /* show */, showInputToken, reason,
statsToken);
@@ -102,7 +104,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public void performHideIme(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
+ public void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
if (curMethod != null) {
@@ -118,11 +120,12 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
if (curMethod.hideSoftInput(hideInputToken, statsToken, 0, resultReceiver)) {
if (DEBUG_IME_VISIBILITY) {
- EventLog.writeEvent(IMF_HIDE_IME, statsToken.getTag(),
- Objects.toString(mService.mCurFocusedWindow),
+ EventLog.writeEvent(IMF_HIDE_IME,
+ statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE,
+ Objects.toString(mService.mImeBindingState.mFocusedWindow),
InputMethodDebug.softInputDisplayReasonToString(reason),
InputMethodDebug.softInputModeToString(
- mService.mCurFocusedWindowSoftInputMode));
+ mService.mImeBindingState.mFocusedWindowSoftInputMode));
}
mService.onShowHideSoftInputRequested(false /* show */, hideInputToken, reason,
statsToken);
@@ -132,14 +135,16 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state) {
- applyImeVisibility(windowToken, statsToken, state, -1 /* ignore reason */);
+ applyImeVisibility(windowToken, statsToken, state,
+ SoftInputShowHideReason.NOT_SET /* ignore reason */);
}
@GuardedBy("ImfLock.class")
void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state, int reason) {
+ @ImeVisibilityStateComputer.VisibilityState int state,
+ @SoftInputShowHideReason int reason) {
switch (state) {
case STATE_SHOW_IME:
ImeTracker.forLogging().onProgress(statsToken,
@@ -164,18 +169,20 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
}
break;
case STATE_HIDE_IME_EXPLICIT:
- mService.hideCurrentInputLocked(windowToken, statsToken, 0, null, reason);
+ mService.hideCurrentInputLocked(windowToken, statsToken,
+ 0 /* flags */, null /* resultReceiver */, reason);
break;
case STATE_HIDE_IME_NOT_ALWAYS:
mService.hideCurrentInputLocked(windowToken, statsToken,
- InputMethodManager.HIDE_NOT_ALWAYS, null, reason);
+ InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason);
break;
case STATE_SHOW_IME_IMPLICIT:
mService.showCurrentInputLocked(windowToken, statsToken,
- InputMethodManager.SHOW_IMPLICIT, null, reason);
+ InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN,
+ null /* resultReceiver */, reason);
break;
case STATE_SHOW_IME_SNAPSHOT:
- showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked(), null);
+ showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked());
break;
case STATE_REMOVE_IME_SNAPSHOT:
removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
@@ -187,11 +194,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
- @Nullable ImeTracker.Token statsToken) {
+ public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
- SHOW_IME_SCREENSHOT_FROM_IMMS, statsToken);
+ SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */);
return true;
}
return false;
@@ -201,8 +207,9 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@Override
public boolean removeImeScreenshot(int displayId) {
if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
- mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow,
- REMOVE_IME_SCREENSHOT_FROM_IMMS, null);
+ mService.onShowHideSoftInputRequested(false /* show */,
+ mService.mImeBindingState.mFocusedWindow,
+ REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */);
return true;
}
return false;
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 776184fb098c..a380bc1ca171 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -201,7 +201,7 @@ final class IInputMethodInvoker {
// TODO(b/192412909): Convert this back to void method
@AnyThread
- boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ boolean showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
try {
mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver);
@@ -214,8 +214,8 @@ final class IInputMethodInvoker {
// TODO(b/192412909): Convert this back to void method
@AnyThread
- boolean hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, int flags,
- ResultReceiver resultReceiver) {
+ boolean hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
+ int flags, ResultReceiver resultReceiver) {
try {
mTarget.hideSoftInput(hideInputToken, statsToken, flags, resultReceiver);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
new file mode 100644
index 000000000000..4c20c3b9784a
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_NAME;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.server.wm.WindowManagerInternal;
+
+/**
+ * Stores information related to one active IME client on one display.
+ */
+final class ImeBindingState {
+
+ /**
+ * The last window token that we confirmed to be focused. This is always updated upon
+ * reports from the input method client. If the window state is already changed before the
+ * report is handled, this field just keeps the last value.
+ */
+ @Nullable
+ final IBinder mFocusedWindow;
+
+ /**
+ * {@link WindowManager.LayoutParams#softInputMode} of {@link #mFocusedWindow}.
+ *
+ * @see #mFocusedWindow
+ */
+ @SoftInputModeFlags
+ final int mFocusedWindowSoftInputMode;
+
+ /**
+ * The client by which {@link #mFocusedWindow} was reported. This gets updated whenever
+ * an
+ * IME-focusable window gained focus (without necessarily starting an input connection),
+ * while {@link InputMethodManagerService#mClient} only gets updated when we actually start an
+ * input connection.
+ *
+ * @see #mFocusedWindow
+ */
+ @Nullable
+ final ClientState mFocusedWindowClient;
+
+ /**
+ * The editor info by which {@link #mFocusedWindow} was reported. This differs from
+ * {@link InputMethodManagerService#mCurEditorInfo} the same way {@link #mFocusedWindowClient}
+ * differs from {@link InputMethodManagerService#mCurClient}.
+ *
+ * @see #mFocusedWindow
+ */
+ @Nullable
+ final EditorInfo mFocusedWindowEditorInfo;
+
+ void dumpDebug(ProtoOutputStream proto, WindowManagerInternal windowManagerInternal) {
+ proto.write(CUR_FOCUSED_WINDOW_NAME,
+ windowManagerInternal.getWindowName(mFocusedWindow));
+ proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE,
+ InputMethodDebug.softInputModeToString(mFocusedWindowSoftInputMode));
+ }
+
+ void dump(String prefix, Printer p) {
+ p.println(prefix + "mFocusedWindow()=" + mFocusedWindow);
+ p.println(prefix + "softInputMode=" + InputMethodDebug.softInputModeToString(
+ mFocusedWindowSoftInputMode));
+ p.println(prefix + "mFocusedWindowClient=" + mFocusedWindowClient);
+ }
+
+ static ImeBindingState newEmptyState() {
+ return new ImeBindingState(
+ /*focusedWindow=*/ null,
+ /*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED,
+ /*focusedWindowClient=*/ null,
+ /*focusedWindowEditorInfo=*/ null
+ );
+ }
+
+ ImeBindingState(@Nullable IBinder focusedWindow,
+ @SoftInputModeFlags int focusedWindowSoftInputMode,
+ @Nullable ClientState focusedWindowClient,
+ @Nullable EditorInfo focusedWindowEditorInfo) {
+ mFocusedWindow = focusedWindow;
+ mFocusedWindowSoftInputMode = focusedWindowSoftInputMode;
+ mFocusedWindowClient = focusedWindowClient;
+ mFocusedWindowEditorInfo = focusedWindowEditorInfo;
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index d06c31cf36f0..85ab77355c9a 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -75,34 +75,12 @@ public final class ImeTrackerService extends IImeTracker.Stub {
@NonNull
@Override
- public ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
+ public ImeTracker.Token onStart(@NonNull String tag, int uid, @ImeTracker.Type int type,
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
final var binder = new Binder();
final var token = new ImeTracker.Token(binder, tag);
- final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN,
- origin, reason, fromUser);
- synchronized (mLock) {
- mHistory.addEntry(binder, entry);
-
- // Register a delayed task to handle the case where the new entry times out.
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT,
- ImeTracker.PHASE_NOT_SET);
- }
- }, TIMEOUT_MS);
- }
- return token;
- }
-
- @NonNull
- @Override
- public ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
- final var binder = new Binder();
- final var token = new ImeTracker.Token(binder, tag);
- final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN,
- origin, reason, fromUser);
+ final var entry = new History.Entry(tag, uid, type, ImeTracker.STATUS_RUN, origin, reason,
+ fromUser);
synchronized (mLock) {
mHistory.addEntry(binder, entry);
@@ -158,7 +136,7 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/**
* Updates the IME request tracking token with new information available in IMMS.
*
- * @param statsToken the token corresponding to the current IME request.
+ * @param statsToken the token tracking the current IME request.
* @param requestWindowName the name of the window that created the IME request.
*/
public void onImmsUpdate(@NonNull ImeTracker.Token statsToken,
@@ -223,7 +201,7 @@ public final class ImeTrackerService extends IImeTracker.Stub {
* Sets the live entry corresponding to the tracking token, if it exists, as finished,
* and uploads the data for metrics.
*
- * @param statsToken the token corresponding to the current IME request.
+ * @param statsToken the token tracking the current IME request.
* @param status the finish status of the IME request.
* @param phase the phase the IME request finished at, if it exists
* (or {@link ImeTracker#PHASE_NOT_SET} otherwise).
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index 29fa36982351..9f2b84d9bfa5 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -17,7 +17,6 @@
package com.android.server.inputmethod;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.view.inputmethod.ImeTracker;
@@ -34,12 +33,12 @@ interface ImeVisibilityApplier {
* Performs showing IME on top of the given window.
*
* @param showInputToken A token that represents the requester to show IME.
- * @param statsToken A token that tracks the progress of an IME request.
+ * @param statsToken The token tracking the current IME request.
* @param resultReceiver If non-null, this will be called back to the caller when
* it has processed request to tell what it has done.
* @param reason The reason for requesting to show IME.
*/
- default void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ default void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {}
@@ -47,12 +46,12 @@ interface ImeVisibilityApplier {
* Performs hiding IME to the given window
*
* @param hideInputToken A token that represents the requester to hide IME.
- * @param statsToken A token that tracks the progress of an IME request.
+ * @param statsToken The token tracking the current IME request.
* @param resultReceiver If non-null, this will be called back to the caller when
* it has processed request to tell what it has done.
* @param reason The reason for requesting to hide IME.
*/
- default void performHideIme(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
+ default void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {}
/**
@@ -60,10 +59,10 @@ interface ImeVisibilityApplier {
* according to the given visibility state.
*
* @param windowToken The token of a window for applying the IME visibility
- * @param statsToken A token that tracks the progress of an IME request.
+ * @param statsToken The token tracking the current IME request.
* @param state The new IME visibility state for the applier to handle
*/
- default void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state) {}
/**
@@ -84,11 +83,9 @@ interface ImeVisibilityApplier {
*
* @param windowToken The token of a window to show the IME screenshot.
* @param displayId The unique id to identify the display
- * @param statsToken A token that tracks the progress of an IME request.
* @return {@code true} if success, {@code false} otherwise.
*/
- default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId,
- @Nullable ImeTracker.Token statsToken) {
+ default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId) {
return false;
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 0dd48ae6c9e1..cdfde87f042f 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -212,9 +212,11 @@ public final class ImeVisibilityStateComputer {
boolean visibleRequested, boolean removed) {
if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed)
&& mCurVisibleImeLayeringOverlay != null) {
- mService.onApplyImeVisibilityFromComputer(imeInputTarget,
- new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
- SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE));
+ final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */);
+ mService.onApplyImeVisibilityFromComputer(imeInputTarget, statsToken,
+ new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason));
}
mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null;
}
@@ -224,7 +226,7 @@ public final class ImeVisibilityStateComputer {
/**
* Called when {@link InputMethodManagerService} is processing the show IME request.
*
- * @param statsToken The token for tracking this show request.
+ * @param statsToken The token tracking the current IME request.
* @return {@code true} when the show request can proceed.
*/
boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken,
@@ -250,7 +252,7 @@ public final class ImeVisibilityStateComputer {
/**
* Called when {@link InputMethodManagerService} is processing the hide IME request.
*
- * @param statsToken The token for tracking this hide request.
+ * @param statsToken The token tracking the current IME request.
* @return {@code true} when the hide request can proceed.
*/
boolean canHideIme(@NonNull ImeTracker.Token statsToken,
@@ -546,7 +548,7 @@ public final class ImeVisibilityStateComputer {
}
}
// Fallback to the focused window for some edge cases (e.g. relaunching the activity)
- return mService.mCurFocusedWindow;
+ return mService.mImeBindingState.mFocusedWindow;
}
IBinder getWindowTokenFrom(ImeTargetWindowState windowState) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 307b70a89634..2205986fe9c9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -28,7 +28,6 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DIS
import static android.server.inputmethod.InputMethodManagerServiceProto.BOUND_TO_METHOD;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ATTRIBUTE;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_CLIENT;
-import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_NAME;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ID;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_METHOD_ID;
@@ -40,7 +39,6 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.IME_WIND
import static android.server.inputmethod.InputMethodManagerServiceProto.IN_FULLSCREEN_MODE;
import static android.server.inputmethod.InputMethodManagerServiceProto.IS_INTERACTIVE;
import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_IME_TARGET_WINDOW_NAME;
-import static android.server.inputmethod.InputMethodManagerServiceProto.LAST_SWITCH_USER_ID;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_IME_WITH_HARD_KEYBOARD;
import static android.server.inputmethod.InputMethodManagerServiceProto.SYSTEM_READY;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -277,9 +275,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@NonNull
private final String[] mNonPreemptibleInputMethods;
- @UserIdInt
- private int mLastSwitchUserId;
-
final Context mContext;
final Resources mRes;
private final Handler mHandler;
@@ -423,6 +418,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private final ClientController mClientController;
/**
+ * Holds the current IME binding state info.
+ */
+ ImeBindingState mImeBindingState;
+
+ /**
* Set once the system is ready to run third party code.
*/
boolean mSystemReady;
@@ -482,13 +482,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private ClientState mCurClient;
/**
- * The last window token that we confirmed to be focused. This is always updated upon reports
- * from the input method client. If the window state is already changed before the report is
- * handled, this field just keeps the last value.
- */
- IBinder mCurFocusedWindow;
-
- /**
* The last window token that we confirmed that IME started talking to. This is always updated
* upon reports from the input method. If the window state is already changed before the report
* is handled, this field just keeps the last value.
@@ -496,34 +489,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
IBinder mLastImeTargetWindow;
/**
- * {@link LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
- *
- * @see #mCurFocusedWindow
- */
- @SoftInputModeFlags
- int mCurFocusedWindowSoftInputMode;
-
- /**
- * The client by which {@link #mCurFocusedWindow} was reported. This gets updated whenever an
- * IME-focusable window gained focus (without necessarily starting an input connection),
- * while {@link #mCurClient} only gets updated when we actually start an input connection.
- *
- * @see #mCurFocusedWindow
- */
- @Nullable
- ClientState mCurFocusedWindowClient;
-
- /**
- * The editor info by which {@link #mCurFocusedWindow} was reported. This differs from
- * {@link #mCurEditorInfo} the same way {@link #mCurFocusedWindowClient} differs
- * from {@link #mCurClient}.
- *
- * @see #mCurFocusedWindow
- */
- @Nullable
- EditorInfo mCurFocusedWindowEditorInfo;
-
- /**
* The {@link IRemoteInputConnection} last provided by the current client.
*/
IRemoteInputConnection mCurInputConnection;
@@ -578,7 +543,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return mBindingController.hasMainConnection();
}
- /** The token tracking the current IME request or {@code null} otherwise. */
+ /**
+ * The token tracking the current IME show request that is waiting for a connection to an IME,
+ * otherwise {@code null}.
+ */
@Nullable
private ImeTracker.Token mCurStatsToken;
@@ -1128,11 +1096,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(
accessibilitySoftKeyboardSetting);
if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
- 0 /* flags */, null /* resultReceiver */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
} else if (isShowRequestedForCurrentWindow()) {
- showCurrentInputImplicitLocked(mCurFocusedWindow,
+ showCurrentInputLocked(mImeBindingState.mFocusedWindow,
+ InputMethodManager.SHOW_IMPLICIT,
SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE);
}
} else if (stylusHandwritingEnabledUri.equals(uri)) {
@@ -1339,20 +1307,35 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void onPackageDataCleared(String packageName, int uid) {
+ final int userId = getChangingUserId();
synchronized (ImfLock.class) {
+ final boolean isCurrentUser = (userId == mSettings.getUserId());
+ final AdditionalSubtypeMap additionalSubtypeMap;
+ final InputMethodSettings settings;
+ if (isCurrentUser) {
+ additionalSubtypeMap = mAdditionalSubtypeMap;
+ settings = mSettings;
+ } else {
+ additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
+ settings = queryInputMethodServicesInternal(mContext, userId,
+ additionalSubtypeMap, DirectBootAwareness.AUTO);
+ }
+
// Note that one package may implement multiple IMEs.
final ArrayList<String> changedImes = new ArrayList<>();
- for (InputMethodInfo imi : mSettings.getMethodList()) {
+ for (InputMethodInfo imi : settings.getMethodList()) {
if (imi.getPackageName().equals(packageName)) {
changedImes.add(imi.getId());
}
}
final AdditionalSubtypeMap newMap =
- mAdditionalSubtypeMap.cloneWithRemoveOrSelf(changedImes);
- if (newMap != mAdditionalSubtypeMap) {
- mAdditionalSubtypeMap = newMap;
+ additionalSubtypeMap.cloneWithRemoveOrSelf(changedImes);
+ if (newMap != additionalSubtypeMap) {
+ if (isCurrentUser) {
+ mAdditionalSubtypeMap = newMap;
+ }
AdditionalSubtypeUtils.save(
- mAdditionalSubtypeMap, mSettings.getMethodMap(), mSettings.getUserId());
+ newMap, settings.getMethodMap(), settings.getUserId());
}
if (!changedImes.isEmpty()) {
mChangedPackages.add(packageName);
@@ -1397,43 +1380,57 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private void onFinishPackageChangesInternal() {
synchronized (ImfLock.class) {
- if (!isChangingPackagesOfCurrentUserLocked()) {
- return;
- }
- if (!shouldRebuildInputMethodListLocked()) {
- return;
+ final int userId = getChangingUserId();
+ final boolean isCurrentUser = (userId == mSettings.getUserId());
+ AdditionalSubtypeMap additionalSubtypeMap;
+ final InputMethodSettings settings;
+ if (isCurrentUser) {
+ additionalSubtypeMap = mAdditionalSubtypeMap;
+ settings = mSettings;
+ } else {
+ additionalSubtypeMap = AdditionalSubtypeUtils.load(userId);
+ settings = queryInputMethodServicesInternal(mContext, userId,
+ additionalSubtypeMap, DirectBootAwareness.AUTO);
}
InputMethodInfo curIm = null;
- String curInputMethodId = mSettings.getSelectedInputMethod();
- final List<InputMethodInfo> methodList = mSettings.getMethodList();
+ String curInputMethodId = settings.getSelectedInputMethod();
+ final List<InputMethodInfo> methodList = settings.getMethodList();
final int numImes = methodList.size();
- if (curInputMethodId != null) {
- for (int i = 0; i < numImes; i++) {
- InputMethodInfo imi = methodList.get(i);
- final String imiId = imi.getId();
- if (imiId.equals(curInputMethodId)) {
- curIm = imi;
- }
-
- int change = isPackageDisappearing(imi.getPackageName());
- if (change == PACKAGE_TEMPORARY_CHANGE
- || change == PACKAGE_PERMANENT_CHANGE) {
- Slog.i(TAG, "Input method uninstalled, disabling: "
- + imi.getComponent());
+ for (int i = 0; i < numImes; i++) {
+ InputMethodInfo imi = methodList.get(i);
+ final String imiId = imi.getId();
+ if (imiId.equals(curInputMethodId)) {
+ curIm = imi;
+ }
+ int change = isPackageDisappearing(imi.getPackageName());
+ if (change == PACKAGE_TEMPORARY_CHANGE || change == PACKAGE_PERMANENT_CHANGE) {
+ Slog.i(TAG, "Input method uninstalled, disabling: " + imi.getComponent());
+ if (isCurrentUser) {
setInputMethodEnabledLocked(imi.getId(), false);
- } else if (change == PACKAGE_UPDATING) {
- Slog.i(TAG,
- "Input method reinstalling, clearing additional subtypes: "
- + imi.getComponent());
- mAdditionalSubtypeMap =
- mAdditionalSubtypeMap.cloneWithRemoveOrSelf(imi.getId());
- AdditionalSubtypeUtils.save(mAdditionalSubtypeMap,
- mSettings.getMethodMap(), mSettings.getUserId());
+ } else {
+ settings.buildAndPutEnabledInputMethodsStrRemovingId(
+ new StringBuilder(),
+ settings.getEnabledInputMethodsAndSubtypeList(),
+ imi.getId());
+ }
+ } else if (change == PACKAGE_UPDATING) {
+ Slog.i(TAG, "Input method reinstalling, clearing additional subtypes: "
+ + imi.getComponent());
+ additionalSubtypeMap =
+ additionalSubtypeMap.cloneWithRemoveOrSelf(imi.getId());
+ AdditionalSubtypeUtils.save(additionalSubtypeMap,
+ settings.getMethodMap(), userId);
+ if (isCurrentUser) {
+ mAdditionalSubtypeMap = additionalSubtypeMap;
}
}
}
+ if (!isCurrentUser || !shouldRebuildInputMethodListLocked()) {
+ return;
+ }
+
buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
boolean changed = false;
@@ -1443,7 +1440,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (change == PACKAGE_TEMPORARY_CHANGE
|| change == PACKAGE_PERMANENT_CHANGE) {
final PackageManager userAwarePackageManager =
- getPackageManagerForUser(mContext, mSettings.getUserId());
+ getPackageManagerForUser(mContext, userId);
ServiceInfo si = null;
try {
si = userAwarePackageManager.getServiceInfo(curIm.getComponent(),
@@ -1613,8 +1610,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
// Hide soft input before user switch task since switch task may block main handler a while
// and delayed the hideCurrentInputLocked().
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */, SoftInputShowHideReason.HIDE_SWITCH_USER);
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_SWITCH_USER);
final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
clientToBeReset);
mUserSwitchHandlerTask = task;
@@ -1663,8 +1660,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final int userId = mActivityManagerInternal.getCurrentUserId();
- mLastSwitchUserId = userId;
-
// mSettings should be created before buildInputMethodListLocked
mSettings = InputMethodSettings.createEmptyMap(userId);
@@ -1688,6 +1683,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mClientController = new ClientController(mPackageManagerInternal);
synchronized (ImfLock.class) {
mClientController.addClientControllerCallback(c -> onClientRemoved(c));
+ mImeBindingState = ImeBindingState.newEmptyState();
}
mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
@@ -1850,7 +1846,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ " selectedIme=" + mSettings.getSelectedInputMethod());
}
- mLastSwitchUserId = newUserId;
if (mIsInteractive && clientToBeReset != null) {
final ClientState cs = mClientController.getClient(clientToBeReset.asBinder());
if (cs == null) {
@@ -2202,8 +2197,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
clearClientSessionLocked(client);
clearClientSessionForAccessibilityLocked(client);
if (mCurClient == client) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
if (mBoundToMethod) {
mBoundToMethod = false;
IInputMethodInvoker curMethod = getCurMethodLocked();
@@ -2216,9 +2211,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
mBoundToAccessibility = false;
mCurClient = null;
- if (mCurFocusedWindowClient == client) {
- mCurFocusedWindowClient = null;
- mCurFocusedWindowEditorInfo = null;
+ if (mImeBindingState.mFocusedWindowClient == client) {
+ mImeBindingState = ImeBindingState.newEmptyState();
}
}
}
@@ -2267,7 +2261,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
void onUnbindCurrentMethodByReset() {
final ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull(
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
if (winState != null && !winState.isRequestedImeVisible()
&& !mVisibilityStateComputer.isInputShown()) {
// Normally, the focus window will apply the IME visibility state to
@@ -2277,7 +2271,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// service, that wouldn't make the attached IME token validity check in time)
// As a result, we have to notify WM to apply IME visibility before clearing the
// binding states in the first place.
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, mCurStatsToken,
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+ SoftInputShowHideReason.UNBIND_CURRENT_METHOD);
+ mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
STATE_HIDE_IME);
}
}
@@ -2311,7 +2307,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
private boolean isShowRequestedForCurrentWindow() {
final ImeTargetWindowState state = mVisibilityStateComputer.getWindowStateOrNull(
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
return state != null && state.isRequestedImeVisible();
}
@@ -2329,10 +2325,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
getCurTokenLocked(),
mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
UserHandle.getUserId(mCurClient.mUid),
- mCurClient.mSelfReportedDisplayId,
- mCurFocusedWindow, mCurEditorInfo, mCurFocusedWindowSoftInputMode,
- getSequenceNumberLocked());
- mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
+ mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
+ mImeBindingState.mFocusedWindowSoftInputMode, getSequenceNumberLocked());
+ mImeTargetWindowMap.put(startInputToken, mImeBindingState.mFocusedWindow);
mStartInputHistory.addEntry(info);
// Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user
@@ -2355,10 +2350,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (isShowRequestedForCurrentWindow()) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
// Re-use current statsToken, if it exists.
- final ImeTracker.Token statsToken = mCurStatsToken;
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsTokenForFocusedClient(true /* show */,
+ SoftInputShowHideReason.ATTACH_NEW_INPUT);
mCurStatsToken = null;
- showCurrentInputLocked(mCurFocusedWindow, statsToken,
- mVisibilityStateComputer.getShowFlags(),
+ showCurrentInputLocked(mImeBindingState.mFocusedWindow, statsToken,
+ mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN,
null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
@@ -2431,7 +2428,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull(
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
if (winState == null) {
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
@@ -2450,8 +2447,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE);
return InputBindResult.NO_IME;
}
@@ -3371,8 +3367,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickTooType, ResultReceiver resultReceiver,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickToolType, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
@@ -3388,7 +3384,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
- return showCurrentInputLocked(windowToken, statsToken, flags, lastClickTooType,
+ return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType,
resultReceiver, reason);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -3620,7 +3616,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
Binder.withCleanCallingIdentity(() -> {
Objects.requireNonNull(windowToken, "windowToken must not be null");
synchronized (ImfLock.class) {
- if (mCurFocusedWindow != windowToken || mCurPerceptible == perceptible) {
+ if (mImeBindingState.mFocusedWindow != windowToken
+ || mCurPerceptible == perceptible) {
return;
}
mCurPerceptible = perceptible;
@@ -3630,24 +3627,18 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- @InputMethodManager.ShowFlags int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ private boolean showCurrentInputLocked(IBinder windowToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) {
+ final var statsToken = createStatsTokenForFocusedClient(true /* show */, reason);
return showCurrentInputLocked(windowToken, statsToken, flags,
- MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
+ MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason);
}
@GuardedBy("ImfLock.class")
- private boolean showCurrentInputLocked(IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickToolType, ResultReceiver resultReceiver,
+ boolean showCurrentInputLocked(IBinder windowToken,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
- // Create statsToken is none exists.
- if (statsToken == null) {
- statsToken = createStatsTokenForFocusedClient(true /* show */,
- ImeTracker.ORIGIN_SERVER_START_INPUT, reason, false /* fromUser */);
- }
-
if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) {
return false;
}
@@ -3685,7 +3676,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3714,17 +3705,29 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- @GuardedBy("ImfLock.class")
- boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- // Create statsToken is none exists.
- if (statsToken == null) {
- final boolean fromUser = reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
- statsToken = createStatsTokenForFocusedClient(false /* show */,
- ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason, fromUser);
+ @Override
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() {
+ super.hideSoftInputFromServerForTest_enforcePermission();
+
+ synchronized (ImfLock.class) {
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT);
}
+ }
+ @GuardedBy("ImfLock.class")
+ private boolean hideCurrentInputLocked(IBinder windowToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) {
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */, reason);
+ return hideCurrentInputLocked(windowToken, statsToken, flags, null /* resultReceiver */,
+ reason);
+ }
+
+ @GuardedBy("ImfLock.class")
+ boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) {
return false;
}
@@ -3883,7 +3886,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final boolean shouldClearFlag =
mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.mUid);
final boolean showForced = mVisibilityStateComputer.mShowForced;
- if (mCurFocusedWindow != windowToken && showForced && shouldClearFlag) {
+ if (mImeBindingState.mFocusedWindow != windowToken
+ && showForced && shouldClearFlag) {
mVisibilityStateComputer.mShowForced = false;
}
@@ -3901,9 +3905,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
+ " a background user, use EditorInfo.targetInputMethodUser with"
+ " INTERACT_ACROSS_USERS_FULL permission.");
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
- 0 /* flags */,
- null /* resultReceiver */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_INVALID_USER);
return InputBindResult.INVALID_USER;
}
@@ -3964,7 +3966,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ " cs=" + cs);
}
- final boolean sameWindowFocused = mCurFocusedWindow == windowToken;
+ final boolean sameWindowFocused = mImeBindingState.mFocusedWindow == windowToken;
final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
final boolean startInputByWinGainedFocus =
(startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0;
@@ -3995,10 +3997,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
null, null, null, null, -1, false);
}
- mCurFocusedWindow = windowToken;
- mCurFocusedWindowSoftInputMode = softInputMode;
- mCurFocusedWindowClient = cs;
- mCurFocusedWindowEditorInfo = editorInfo;
+ mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo);
mCurPerceptible = true;
// We want to start input before showing the IME, but after closing
@@ -4011,11 +4010,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.computeState(windowState,
isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags));
if (imeVisRes != null) {
+ boolean isShow = false;
switch (imeVisRes.getReason()) {
case SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY:
case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV:
case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE:
+ isShow = true;
+
if (editorInfo != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, editorInfo, startInputFlags,
@@ -4025,10 +4027,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
break;
}
-
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null /* statsToken */,
+ final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason());
+ mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
imeVisRes.getState(), imeVisRes.getReason());
-
if (imeVisRes.getReason() == SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW) {
// If focused display changed, we should unbind current method
// to make app window in previous display relayout after Ime
@@ -4054,13 +4055,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- private void showCurrentInputImplicitLocked(@NonNull IBinder windowToken,
- @SoftInputShowHideReason int reason) {
- showCurrentInputLocked(windowToken, null /* statsToken */, InputMethodManager.SHOW_IMPLICIT,
- null /* resultReceiver */, reason);
- }
-
- @GuardedBy("ImfLock.class")
private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName,
@Nullable ImeTracker.Token statsToken) {
if (mCurClient == null || client == null
@@ -4074,7 +4068,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
- if (!isImeClientFocused(mCurFocusedWindow, cs)) {
+ if (!isImeClientFocused(mImeBindingState.mFocusedWindow, cs)) {
Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client));
return false;
}
@@ -4086,8 +4080,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
final int uid = Binder.getCallingUid();
- if (mCurFocusedWindowClient != null && client != null
- && mCurFocusedWindowClient.mClient.asBinder() == client.asBinder()) {
+ if (mImeBindingState.mFocusedWindowClient != null && client != null
+ && mImeBindingState.mFocusedWindowClient.mClient.asBinder() == client.asBinder()) {
return true;
}
if (mSettings.getUserId() != UserHandle.getUserId(uid)) {
@@ -4710,12 +4704,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked());
proto.write(CUR_SEQ, getSequenceNumberLocked());
proto.write(CUR_CLIENT, Objects.toString(mCurClient));
- proto.write(CUR_FOCUSED_WINDOW_NAME,
- mWindowManagerInternal.getWindowName(mCurFocusedWindow));
+ mImeBindingState.dumpDebug(proto, mWindowManagerInternal);
proto.write(LAST_IME_TARGET_WINDOW_NAME,
mWindowManagerInternal.getWindowName(mLastImeTargetWindow));
- proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE,
- InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode));
+ proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString(
+ mImeBindingState.mFocusedWindowSoftInputMode));
if (mCurEditorInfo != null) {
mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE);
}
@@ -4725,7 +4718,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked()));
proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId);
proto.write(SYSTEM_READY, mSystemReady);
- proto.write(LAST_SWITCH_USER_ID, mLastSwitchUserId);
proto.write(HAVE_CONNECTION, hasConnectionLocked());
proto.write(BOUND_TO_METHOD, mBoundToMethod);
proto.write(IS_INTERACTIVE, mIsInteractive);
@@ -4762,15 +4754,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
ImeTracker.forLogging().onFailed(statsToken,
- ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(
windowToken);
mVisibilityApplier.applyImeVisibility(requestToken, statsToken,
@@ -4835,12 +4829,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken);
final WindowManagerInternal.ImeTargetInfo info =
mWindowManagerInternal.onToggleImeRequested(
- show, mCurFocusedWindow, requestToken, mCurTokenDisplayId);
+ show, mImeBindingState.mFocusedWindow, requestToken, mCurTokenDisplayId);
mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
- mCurFocusedWindowClient, mCurFocusedWindowEditorInfo, info.focusedWindowName,
- mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
- info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
- info.imeSurfaceParentName));
+ mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo,
+ info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason,
+ mInFullscreenMode, info.requestWindowName, info.imeControlTargetName,
+ info.imeLayerTargetName, info.imeSurfaceParentName));
if (statsToken != null) {
mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName);
@@ -4848,17 +4842,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- private void hideMySoftInput(@NonNull IBinder token, @InputMethodManager.HideFlags int flags,
- @SoftInputShowHideReason int reason) {
+ private void hideMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
final long ident = Binder.clearCallingIdentity();
try {
- hideCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags,
+ hideCurrentInputLocked(mLastImeTargetWindow, statsToken, flags,
null /* resultReceiver */, reason);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4870,18 +4868,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- private void showMySoftInput(@NonNull IBinder token, @InputMethodManager.ShowFlags int flags) {
+ private void showMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
final long ident = Binder.clearCallingIdentity();
try {
- showCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags,
- null /* resultReceiver */,
- SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME);
+ showCurrentInputLocked(mLastImeTargetWindow, statsToken, flags,
+ MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4898,10 +4900,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- void onApplyImeVisibilityFromComputer(IBinder windowToken,
+ void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
@NonNull ImeVisibilityResult result) {
synchronized (ImfLock.class) {
- mVisibilityApplier.applyImeVisibility(windowToken, null, result.getState(),
+ mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(),
result.getReason());
}
}
@@ -5002,10 +5004,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case MSG_HIDE_ALL_INPUT_METHODS:
synchronized (ImfLock.class) {
- final @SoftInputShowHideReason int reason = (int) msg.obj;
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */, reason);
-
+ @SoftInputShowHideReason final int reason = (int) msg.obj;
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, reason);
}
return true;
case MSG_REMOVE_IME_SURFACE: {
@@ -5024,7 +5024,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
IBinder windowToken = (IBinder) msg.obj;
synchronized (ImfLock.class) {
try {
- if (windowToken == mCurFocusedWindow
+ if (windowToken == mImeBindingState.mFocusedWindow
&& mEnabledSession != null && mEnabledSession.mSession != null) {
mEnabledSession.mSession.removeImeSurface();
}
@@ -5099,7 +5099,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case MSG_START_HANDWRITING:
synchronized (ImfLock.class) {
IInputMethodInvoker curMethod = getCurMethodLocked();
- if (curMethod == null || mCurFocusedWindow == null) {
+ if (curMethod == null || mImeBindingState.mFocusedWindow == null) {
return true;
}
final HandwritingModeController.HandwritingSession session =
@@ -5107,7 +5107,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
msg.arg1 /*requestId*/,
msg.arg2 /*pid*/,
mBindingController.getCurMethodUid(),
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
if (session == null) {
Slog.e(TAG,
"Failed to start handwriting session for requestId: " + msg.arg1);
@@ -5160,10 +5160,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Handle IME visibility when interactive changed before finishing the input to
// ensure we preserve the last state as possible.
final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
- mCurFocusedWindow, interactive);
+ mImeBindingState.mFocusedWindow, interactive);
if (imeVisRes != null) {
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null,
- imeVisRes.getState(), imeVisRes.getReason());
+ // Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker.
+ mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow,
+ null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason());
}
// Eligible IME processes use new "setInteractive" protocol.
mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode);
@@ -5853,7 +5854,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void reportImeControl(@Nullable IBinder windowToken) {
synchronized (ImfLock.class) {
- if (mCurFocusedWindow != windowToken) {
+ if (mImeBindingState.mFocusedWindow != windowToken) {
// mCurPerceptible was set by the focused window, but it is no longer in
// control, so we reset mCurPerceptible.
mCurPerceptible = true;
@@ -5867,7 +5868,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Hide the IME method menu only when the IME surface parent is changed by the
// input target changed, in case seeing the dialog dismiss flickering during
// the next focused window starting the input connection.
- if (mLastImeTargetWindow != mCurFocusedWindow) {
+ if (mLastImeTargetWindow != mImeBindingState.mFocusedWindow) {
mMenuController.hideInputMethodMenuLocked();
}
}
@@ -6161,11 +6162,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
p.println(" mCurPerceptible=" + mCurPerceptible);
- p.println(" mCurFocusedWindow=" + mCurFocusedWindow
- + " softInputMode="
- + InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
- + " client=" + mCurFocusedWindowClient);
- focusedWindowClient = mCurFocusedWindowClient;
+ mImeBindingState.dump(" ", p);
p.println(" mCurId=" + getCurIdLocked() + " mHaveConnection=" + hasConnectionLocked()
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
+ mBindingController.isVisibleBound());
@@ -6216,7 +6213,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
p.println("No input method client.");
}
- if (focusedWindowClient != null && client != focusedWindowClient) {
+ if (mImeBindingState.mFocusedWindowClient != null
+ && client != mImeBindingState.mFocusedWindowClient) {
p.println(" ");
p.println("Warning: Current input method client doesn't match the last focused. "
+ "window.");
@@ -6224,7 +6222,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
p.println(" ");
pw.flush();
try {
- TransferPipe.dumpAsync(focusedWindowClient.mClient.asBinder(), fd, args);
+ TransferPipe.dumpAsync(
+ mImeBindingState.mFocusedWindowClient.mClient.asBinder(), fd, args);
} catch (IOException | RemoteException e) {
p.println("Failed to dump input method client in focused window: " + e);
}
@@ -6296,8 +6295,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@ShellCommandResult
private int onCommandWithSystemIdentity(@Nullable String cmd) {
switch (TextUtils.emptyIfNull(cmd)) {
- case "get-last-switch-user-id":
- return mService.getLastSwitchUserId(this);
case "tracing":
return mService.handleShellCommandTraceInputMethod(this);
case "ime": { // For "adb shell ime <command>".
@@ -6411,15 +6408,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// ----------------------------------------------------------------------
// Shell command handlers:
- @BinderThread
- @ShellCommandResult
- private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) {
- synchronized (ImfLock.class) {
- shellCommand.getOutPrintWriter().println(mLastSwitchUserId);
- return ShellCommandResult.SUCCESS;
- }
- }
-
/**
* Handles {@code adb shell ime list}.
* @param shellCommand {@link ShellCommand} object that is handling this command.
@@ -6670,8 +6658,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getUserId()) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
- 0 /* flags */, null /* resultReceiver */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
mBindingController.unbindCurrentMethod();
@@ -6800,27 +6787,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
* Creates an IME request tracking token for the current focused client.
*
* @param show whether this is a show or a hide request.
- * @param origin the origin of the IME request.
* @param reason the reason why the IME request was created.
- * @param fromUser whether this request was created directly from user interaction.
*/
@NonNull
private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
- final int uid = mCurFocusedWindowClient != null
- ? mCurFocusedWindowClient.mUid
+ @SoftInputShowHideReason int reason) {
+ final int uid = mImeBindingState.mFocusedWindowClient != null
+ ? mImeBindingState.mFocusedWindowClient.mUid
: -1;
- final var packageName = mCurFocusedWindowEditorInfo != null
- ? mCurFocusedWindowEditorInfo.packageName
+ final var packageName = mImeBindingState.mFocusedWindowEditorInfo != null
+ ? mImeBindingState.mFocusedWindowEditorInfo.packageName
: "uid(" + uid + ")";
- if (show) {
- return ImeTracker.forLogging()
- .onRequestShow(packageName, uid, origin, reason, fromUser);
- } else {
- return ImeTracker.forLogging()
- .onRequestHide(packageName, uid, origin, reason, fromUser);
- }
+ return ImeTracker.forLogging().onStart(packageName, uid,
+ show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_SERVER,
+ reason, false /* fromUser */);
}
private static final class InputMethodPrivilegedOperationsImpl
@@ -6895,12 +6876,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
- @SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) {
+ public void hideMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason,
+ AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {
- mImms.hideMySoftInput(mToken, flags, reason);
+ mImms.hideMySoftInput(mToken, statsToken, flags, reason);
typedFuture.complete(null);
} catch (Throwable e) {
typedFuture.completeExceptionally(e);
@@ -6909,12 +6891,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void showMySoftInput(@InputMethodManager.ShowFlags int flags,
+ public void showMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason,
AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {
- mImms.showMySoftInput(mToken, flags);
+ mImms.showMySoftInput(mToken, statsToken, flags, reason);
typedFuture.complete(null);
} catch (Throwable e) {
typedFuture.completeExceptionally(e);
@@ -6973,7 +6956,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
mImms.applyImeVisibility(mToken, windowToken, setVisible, statsToken);
}
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index 62d44557ae4c..9caf5cfe67f3 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -72,6 +72,7 @@ import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.view.IInputMethodManager;
import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -183,6 +184,13 @@ public class ZeroJankProxy extends IInputMethodManager.Stub {
return true;
}
+ @Override
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() throws RemoteException {
+ super.hideSoftInputFromServerForTest_enforcePermission();
+ mInner.hideSoftInputFromServerForTest();
+ }
+
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@Override
public void startInputOrWindowGainedFocusAsync(
@@ -404,6 +412,13 @@ public class ZeroJankProxy extends IInputMethodManager.Stub {
in, out, err, args, callback, resultReceiver);
}
+ @Override
+ protected void dump(@NonNull FileDescriptor fd,
+ @NonNull PrintWriter fout,
+ @Nullable String[] args) {
+ ((InputMethodManagerService) mInner).dump(fd, fout, args);
+ }
+
private void sendOnStartInputResult(
IInputMethodClient client, InputBindResult res, int startInputSeq) {
InputMethodManagerService service = (InputMethodManagerService) mInner;
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 2522f7b82353..470cc049a82b 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -109,20 +109,15 @@ public final class StationaryThrottlingLocationProvider extends DelegateLocation
synchronized (mLock) {
mDeviceIdleHelper.addListener(this);
- mDeviceIdle = mDeviceIdleHelper.isDeviceIdle();
- mDeviceStationaryHelper.addListener(this);
- mDeviceStationary = false;
- mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
-
- onThrottlingChangedLocked(false);
+ onDeviceIdleChanged(mDeviceIdleHelper.isDeviceIdle());
}
}
@Override
protected void onStop() {
synchronized (mLock) {
- mDeviceStationaryHelper.removeListener(this);
mDeviceIdleHelper.removeListener(this);
+ onDeviceIdleChanged(false);
mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
@@ -155,13 +150,26 @@ public final class StationaryThrottlingLocationProvider extends DelegateLocation
}
mDeviceIdle = deviceIdle;
- onThrottlingChangedLocked(false);
+ if (deviceIdle) {
+ // device stationary helper will deliver an immediate listener update
+ mDeviceStationaryHelper.addListener(this);
+ } else {
+ mDeviceStationaryHelper.removeListener(this);
+ mDeviceStationary = false;
+ mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+ onThrottlingChangedLocked(false);
+ }
}
}
@Override
public void onDeviceStationaryChanged(boolean deviceStationary) {
synchronized (mLock) {
+ if (!mDeviceIdle) {
+ // stationary detection is only registered while idle - ignore late notifications
+ return;
+ }
+
if (mDeviceStationary == deviceStationary) {
return;
}
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index a597edd93515..8fdc22b81769 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -59,7 +59,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider implements
private static final String EXTRA_LOCATION_TAGS = "android:location_allow_listed_tags";
private static final String LOCATION_TAGS_SEPARATOR = ";";
- private static final long RESET_DELAY_MS = 1000;
+ private static final long RESET_DELAY_MS = 10000;
/**
* Creates and registers this proxy. If no suitable service is available for the proxy, returns
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9a76ebd148aa..29ea0713e0a8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -108,7 +108,6 @@ import android.provider.DeviceConfig;
import android.provider.Settings;
import android.security.AndroidKeyStoreMaintenance;
import android.security.Authorization;
-import android.security.KeyStore;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -169,6 +168,7 @@ import java.io.PrintWriter;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -292,7 +292,7 @@ public class LockSettingsService extends ILockSettings.Stub {
private final IActivityManager mActivityManager;
private final SyntheticPasswordManager mSpManager;
- private final java.security.KeyStore mJavaKeyStore;
+ private final KeyStore mKeyStore;
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache;
@@ -564,10 +564,6 @@ public class LockSettingsService extends ILockSettings.Stub {
return DeviceStateCache.getInstance();
}
- public KeyStore getKeyStore() {
- return KeyStore.getInstance();
- }
-
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
return RecoverableKeyStoreManager.getInstance(mContext);
}
@@ -619,9 +615,9 @@ public class LockSettingsService extends ILockSettings.Stub {
return (BiometricManager) mContext.getSystemService(Context.BIOMETRIC_SERVICE);
}
- public java.security.KeyStore getJavaKeyStore() {
+ public KeyStore getKeyStore() {
try {
- java.security.KeyStore ks = java.security.KeyStore.getInstance(
+ KeyStore ks = KeyStore.getInstance(
SyntheticPasswordCrypto.androidKeystoreProviderName());
ks.load(new AndroidKeyStoreLoadStoreParameter(
SyntheticPasswordCrypto.keyNamespace()));
@@ -631,8 +627,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
- java.security.KeyStore ks) {
+ public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
return new UnifiedProfilePasswordCache(ks);
}
@@ -654,7 +649,7 @@ public class LockSettingsService extends ILockSettings.Stub {
protected LockSettingsService(Injector injector) {
mInjector = injector;
mContext = injector.getContext();
- mJavaKeyStore = injector.getJavaKeyStore();
+ mKeyStore = injector.getKeyStore();
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler(injector.getServiceThread());
mStrongAuth = injector.getStrongAuth();
@@ -676,7 +671,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mGatekeeperPasswords = new LongSparseArray<>();
mSpManager = injector.getSyntheticPasswordManager(mStorage);
- mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore);
+ mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mKeyStore);
mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
@@ -1482,7 +1477,7 @@ public class LockSettingsService extends ILockSettings.Stub {
byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
storedData.length);
byte[] decryptionResult;
- SecretKey decryptionKey = (SecretKey) mJavaKeyStore.getKey(
+ SecretKey decryptionKey = (SecretKey) mKeyStore.getKey(
PROFILE_KEY_NAME_DECRYPT + userId, null);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
@@ -1875,9 +1870,10 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
- updatePasswordHistory(newCredential, userHandle);
- mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
+ private void onPostPasswordChanged(LockscreenCredential newCredential, int userId) {
+ updatePasswordHistory(newCredential, userId);
+ mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userId);
+ sendMainUserCredentialChangedNotificationIfNeeded(userId);
}
/**
@@ -2076,16 +2072,16 @@ public class LockSettingsService extends ILockSettings.Stub {
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
try {
- mJavaKeyStore.setEntry(
+ mKeyStore.setEntry(
PROFILE_KEY_NAME_ENCRYPT + profileUserId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- mJavaKeyStore.setEntry(
+ mKeyStore.setEntry(
PROFILE_KEY_NAME_DECRYPT + profileUserId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
@@ -2094,7 +2090,7 @@ public class LockSettingsService extends ILockSettings.Stub {
.setUserAuthenticationValidityDurationSeconds(30)
.build());
// Key imported, obtain a reference to it.
- SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey(
+ SecretKey keyStoreEncryptionKey = (SecretKey) mKeyStore.getKey(
PROFILE_KEY_NAME_ENCRYPT + profileUserId, null);
Cipher cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
@@ -2104,7 +2100,7 @@ public class LockSettingsService extends ILockSettings.Stub {
iv = cipher.getIV();
} finally {
// The original key can now be discarded.
- mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
+ mKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
}
} catch (UnrecoverableKeyException
| BadPaddingException | IllegalBlockSizeException | KeyStoreException
@@ -2556,11 +2552,10 @@ public class LockSettingsService extends ILockSettings.Stub {
final String encryptAlias = PROFILE_KEY_NAME_ENCRYPT + targetUserId;
final String decryptAlias = PROFILE_KEY_NAME_DECRYPT + targetUserId;
try {
- if (mJavaKeyStore.containsAlias(encryptAlias) ||
- mJavaKeyStore.containsAlias(decryptAlias)) {
+ if (mKeyStore.containsAlias(encryptAlias) || mKeyStore.containsAlias(decryptAlias)) {
Slogf.i(TAG, "Removing keystore profile key for user %d", targetUserId);
- mJavaKeyStore.deleteEntry(encryptAlias);
- mJavaKeyStore.deleteEntry(decryptAlias);
+ mKeyStore.deleteEntry(encryptAlias);
+ mKeyStore.deleteEntry(decryptAlias);
}
} catch (KeyStoreException e) {
// We have tried our best to remove the key.
@@ -3062,7 +3057,6 @@ public class LockSettingsService extends ILockSettings.Stub {
setCurrentLskfBasedProtectorId(newProtectorId, userId);
LockPatternUtils.invalidateCredentialTypeCache();
synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
- sendMainUserCredentialChangedNotificationIfNeeded(userId);
setUserPasswordMetrics(credential, userId);
mUnifiedProfilePasswordCache.removePassword(userId);
@@ -3457,7 +3451,7 @@ public class LockSettingsService extends ILockSettings.Stub {
private void dumpKeystoreKeys(IndentingPrintWriter pw) {
try {
- final Enumeration<String> aliases = mJavaKeyStore.aliases();
+ final Enumeration<String> aliases = mKeyStore.aliases();
while (aliases.hasMoreElements()) {
pw.println(aliases.nextElement());
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 1f7d5490dd6d..2a487856bfb8 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -851,7 +851,6 @@ class MediaRouter2ServiceImpl {
}
}
- @RequiresPermission(value = Manifest.permission.MEDIA_ROUTING_CONTROL, conditional = true)
private boolean checkMediaRoutingControlPermission(
int callerUid, int callerPid, @Nullable String callerPackageName) {
return PermissionChecker.checkPermissionForDataDelivery(
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index a110e5637f82..1dc86f2b7f1e 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -35,12 +35,11 @@ import java.io.PrintWriter;
* Keeps the record of {@link Session2Token} to help send command to the corresponding session.
*/
// TODO(jaewan): Do not call service method directly -- introduce listener instead.
-public class MediaSession2Record implements MediaSessionRecordImpl {
+public class MediaSession2Record extends MediaSessionRecordImpl {
private static final String TAG = "MediaSession2Record";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Object mLock = new Object();
- private final int mUniqueId;
@GuardedBy("mLock")
private final Session2Token mSessionToken;
@GuardedBy("mLock")
@@ -57,20 +56,18 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
private boolean mIsClosed;
private final int mPid;
- private final ForegroundServiceDelegationOptions mForegroundServiceDelegationOptions;
public MediaSession2Record(
Session2Token sessionToken,
MediaSessionService service,
Looper handlerLooper,
int pid,
- int policies,
- int uniqueId) {
+ int policies) {
// The lock is required to prevent `Controller2Callback` from using partially initialized
// `MediaSession2Record.this`.
synchronized (mLock) {
+ mUniqueId = sNextMediaSessionRecordId.getAndIncrement();
mSessionToken = sessionToken;
- mUniqueId = uniqueId;
mService = service;
mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
mController = new MediaController2.Builder(service.getContext(), sessionToken)
@@ -78,32 +75,6 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
.build();
mPid = pid;
mPolicies = policies;
- mForegroundServiceDelegationOptions =
- new ForegroundServiceDelegationOptions.Builder()
- .setClientPid(mPid)
- .setClientUid(getUid())
- .setClientPackageName(getPackageName())
- .setClientAppThread(null)
- .setSticky(false)
- .setClientInstanceName(
- "MediaSessionFgsDelegate_"
- + getUid()
- + "_"
- + mPid
- + "_"
- + getPackageName())
- .setForegroundServiceTypes(0)
- .setDelegationService(
- ForegroundServiceDelegationOptions
- .DELEGATION_SERVICE_MEDIA_PLAYBACK)
- .build();
- }
- }
-
- @Override
- public int getUniqueId() {
- synchronized (mLock) {
- return mUniqueId;
}
}
@@ -128,7 +99,10 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
- return mForegroundServiceDelegationOptions;
+ // For an app to be eligible for FGS delegation, it needs a media session liked to a media
+ // notification. Currently, notifications cannot be linked to MediaSession2 so it is not
+ // supported.
+ return null;
}
@Override
@@ -210,7 +184,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "uniqueId=" + mUniqueId);
+ pw.println(prefix + "uniqueId=" + getUniqueId());
pw.println(prefix + "token=" + mSessionToken);
pw.println(prefix + "controller=" + mController);
@@ -220,7 +194,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public String toString() {
- return getPackageName() + "/" + mUniqueId + " (userId=" + getUserId() + ")";
+ return getPackageName() + "/" + getUniqueId() + " (userId=" + getUserId() + ")";
}
private class Controller2Callback extends MediaController2.ControllerCallback {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 15527041d8eb..a9a82725223d 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -96,7 +96,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
* MediaSession wrapper class instead.
*/
// TODO(jaewan): Do not call service method directly -- introduce listener instead.
-public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {
+public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinder.DeathRecipient {
/**
* {@link android.media.session.MediaSession#setMediaButtonBroadcastReceiver(
@@ -173,7 +173,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private final int mUserId;
private final String mPackageName;
private final String mTag;
- private final int mUniqueId;
private final Bundle mSessionInfo;
private final ControllerStub mController;
private final MediaSession.Token mSessionToken;
@@ -231,18 +230,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
String ownerPackageName,
ISessionCallback cb,
String tag,
- int uniqueId,
Bundle sessionInfo,
MediaSessionService service,
Looper handlerLooper,
int policies)
throws RemoteException {
+ mUniqueId = sNextMediaSessionRecordId.getAndIncrement();
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
mPackageName = ownerPackageName;
mTag = tag;
- mUniqueId = uniqueId;
mSessionInfo = sessionInfo;
mController = new ControllerStub();
mSessionToken = new MediaSession.Token(ownerUid, mController);
@@ -303,16 +301,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
/**
- * Get the unique id of this session record.
- *
- * @return a unique id of this session record.
- */
- @Override
- public int getUniqueId() {
- return mUniqueId;
- }
-
- /**
* Get the info for this session.
*
* @return Info that identifies this session.
@@ -724,7 +712,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@Override
public String toString() {
- return mPackageName + "/" + mTag + "/" + mUniqueId + " (userId=" + mUserId + ")";
+ return mPackageName + "/" + mTag + "/" + getUniqueId() + " (userId=" + mUserId + ")";
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index e53a2dbe8101..e4b2fad5f309 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -25,39 +25,37 @@ import android.view.KeyEvent;
import com.android.server.media.MediaSessionPolicyProvider.SessionPolicy;
import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Common interfaces between {@link MediaSessionRecord} and {@link MediaSession2Record}.
*/
-public interface MediaSessionRecordImpl extends AutoCloseable {
+public abstract class MediaSessionRecordImpl {
- /**
- * Get the unique id of this session record.
- *
- * @return a unique id of this session record.
- */
- int getUniqueId();
+ static final AtomicInteger sNextMediaSessionRecordId = new AtomicInteger(1);
+ int mUniqueId;
/**
* Get the info for this session.
*
* @return Info that identifies this session.
*/
- String getPackageName();
+ public abstract String getPackageName();
/**
* Get the UID this session was created for.
*
* @return The UID for this session.
*/
- int getUid();
+ public abstract int getUid();
/**
* Get the user id this session was created for.
*
* @return The user id for this session.
*/
- int getUserId();
+ public abstract int getUserId();
/**
* Get the {@link ForegroundServiceDelegationOptions} needed for notifying activity manager
@@ -66,7 +64,7 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
* @return the {@link ForegroundServiceDelegationOptions} needed for notifying the activity
* manager service with changes in the {@link PlaybackState} for this session.
*/
- ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions();
+ public abstract ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions();
/**
* Check if this session has system priority and should receive media buttons before any other
@@ -74,7 +72,7 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
*
* @return True if this is a system priority session, false otherwise
*/
- boolean isSystemPriority();
+ public abstract boolean isSystemPriority();
/**
* Send a volume adjustment to the session owner. Direction must be one of
@@ -95,7 +93,7 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
* @param useSuggested True to use adjustSuggestedStreamVolumeForUid instead of
* adjustStreamVolumeForUid
*/
- void adjustVolume(String packageName, String opPackageName, int pid, int uid,
+ public abstract void adjustVolume(String packageName, String opPackageName, int pid, int uid,
boolean asSystemService, int direction, int flags, boolean useSuggested);
/**
@@ -105,7 +103,7 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
* @return True if the session is active, false otherwise.
*/
// TODO(jaewan): Find better naming, or remove this from the MediaSessionRecordImpl.
- boolean isActive();
+ public abstract boolean isActive();
/**
* Check if the session's playback active state matches with the expectation. This always
@@ -115,7 +113,7 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
* @param expected True if playback is expected to be active. False otherwise.
* @return True if the session's playback matches with the expectation. False otherwise.
*/
- boolean checkPlaybackActiveState(boolean expected);
+ public abstract boolean checkPlaybackActiveState(boolean expected);
/**
* Check whether the playback type is local or remote.
@@ -128,7 +126,7 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
*
* @return {@code true} if the playback is local. {@code false} if the playback is remote.
*/
- boolean isPlaybackTypeLocal();
+ public abstract boolean isPlaybackTypeLocal();
/**
* Sends media button.
@@ -145,27 +143,27 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
* @return {@code true} if the attempt to send media button was successfully.
* {@code false} otherwise.
*/
- boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
- KeyEvent ke, int sequenceId, ResultReceiver cb);
+ public abstract boolean sendMediaButton(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent ke, int sequenceId, ResultReceiver cb);
/**
* Returns whether the media session can handle volume key events.
*
* @return True if this media session can handle volume key events, false otherwise.
*/
- boolean canHandleVolumeKey();
+ public abstract boolean canHandleVolumeKey();
/**
* Get session policies from custom policy provider set when MediaSessionRecord is instantiated.
* If custom policy does not exist, will return null.
*/
@SessionPolicy
- int getSessionPolicies();
+ public abstract int getSessionPolicies();
/**
* Overwrite session policies that have been set when MediaSessionRecord is instantiated.
*/
- void setSessionPolicies(@SessionPolicy int policies);
+ public abstract void setSessionPolicies(@SessionPolicy int policies);
/**
* Dumps internal state
@@ -173,16 +171,37 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
* @param pw print writer
* @param prefix prefix
*/
- void dump(PrintWriter pw, String prefix);
+ public abstract void dump(PrintWriter pw, String prefix);
/**
- * Override {@link AutoCloseable#close} to tell it not to throw exception.
+ * Similar to {@link AutoCloseable#close} without throwing an exception.
*/
- @Override
- void close();
+ public abstract void close();
+
+ /**
+ * Get the unique id of this session record.
+ *
+ * @return a unique id of this session record.
+ */
+ public int getUniqueId() {
+ return mUniqueId;
+ }
/**
* Returns whether {@link #close()} is called before.
*/
- boolean isClosed();
+ public abstract boolean isClosed();
+
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof MediaSessionRecordImpl)) return false;
+ MediaSessionRecordImpl that = (MediaSessionRecordImpl) o;
+ return mUniqueId == that.mUniqueId;
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(mUniqueId);
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9e98a5809650..e2163c54f4e2 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -106,7 +106,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* System implementation of MediaSessionManager
@@ -128,6 +127,22 @@ public class MediaSessionService extends SystemService implements Monitor {
*/
private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
+ /**
+ * Action reported to UsageStatsManager when a media session becomes active and user engaged
+ * for a given app. App is expected to show an ongoing notification after this.
+ */
+ private static final String USAGE_STATS_ACTION_START = "start";
+
+ /**
+ * Action reported to UsageStatsManager when a media session is no longer active and user
+ * engaged for a given app. If media session only pauses for a brief time the event will not
+ * necessarily be reported in case user is still "engaging" and will restart it momentarily.
+ * In such case, action may be reported after a short delay to ensure user is truly no longer
+ * engaging. Afterwards, the app is no longer expected to show an ongoing notification.
+ */
+ private static final String USAGE_STATS_ACTION_STOP = "stop";
+ private static final String USAGE_STATS_CATEGORY = "android.media";
+
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
@@ -154,9 +169,8 @@ public class MediaSessionService extends SystemService implements Monitor {
private UsageStatsManagerInternal mUsageStatsManagerInternal;
/* Maps uid with all user engaging session tokens associated to it */
- private final SparseArray<Set<MediaSession.Token>> mUserEngagingSessions = new SparseArray<>();
-
- private final AtomicInteger mNextMediaSessionRecordId = new AtomicInteger(1);
+ private final SparseArray<Set<MediaSessionRecordImpl>> mUserEngagingSessions =
+ new SparseArray<>();
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -196,8 +210,7 @@ public class MediaSessionService extends SystemService implements Monitor {
MediaSessionService.this,
mRecordThread.getLooper(),
pid,
- /* policies= */ 0,
- /* uniqueId= */ mNextMediaSessionRecordId.getAndIncrement());
+ /* policies= */ 0);
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
if (user != null) {
@@ -613,9 +626,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
record.getForegroundServiceDelegationOptions();
- if (foregroundServiceDelegationOptions == null
- || foregroundServiceDelegationOptions.mClientPid == Process.INVALID_PID) {
- // This record doesn't support FGS delegation. In practice, this is MediaSession2.
+ if (foregroundServiceDelegationOptions == null) {
return;
}
if (allowRunningInForeground) {
@@ -628,24 +639,24 @@ public class MediaSessionService extends SystemService implements Monitor {
}
private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) {
- if (!android.app.usage.Flags.userInteractionTypeApi()
- || !(record instanceof MediaSessionRecord)) {
+ if (!android.app.usage.Flags.userInteractionTypeApi()) {
return;
}
String packageName = record.getPackageName();
int sessionUid = record.getUid();
- MediaSession.Token token = ((MediaSessionRecord) record).getSessionToken();
if (userEngaged) {
if (!mUserEngagingSessions.contains(sessionUid)) {
mUserEngagingSessions.put(sessionUid, new HashSet<>());
- reportUserInteractionEvent(/* action= */ "start", record.getUserId(), packageName);
+ reportUserInteractionEvent(
+ USAGE_STATS_ACTION_START, record.getUserId(), packageName);
}
- mUserEngagingSessions.get(sessionUid).add(token);
+ mUserEngagingSessions.get(sessionUid).add(record);
} else if (mUserEngagingSessions.contains(sessionUid)) {
- mUserEngagingSessions.get(sessionUid).remove(token);
+ mUserEngagingSessions.get(sessionUid).remove(record);
if (mUserEngagingSessions.get(sessionUid).isEmpty()) {
- reportUserInteractionEvent(/* action= */ "stop", record.getUserId(), packageName);
+ reportUserInteractionEvent(
+ USAGE_STATS_ACTION_STOP, record.getUserId(), packageName);
mUserEngagingSessions.remove(sessionUid);
}
}
@@ -653,7 +664,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private void reportUserInteractionEvent(String action, int userId, String packageName) {
PersistableBundle extras = new PersistableBundle();
- extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media");
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, USAGE_STATS_CATEGORY);
extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
mUsageStatsManagerInternal.reportUserInteractionEvent(packageName, userId, extras);
}
@@ -806,7 +817,6 @@ public class MediaSessionService extends SystemService implements Monitor {
callerPackageName,
cb,
tag,
- /* uniqueId= */ mNextMediaSessionRecordId.getAndIncrement(),
sessionInfo,
this,
mRecordThread.getLooper(),
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 31bfc6954416..18b495bfce5d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1252,7 +1252,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (isUidStateChangeRelevant(callbackInfo, procState, procStateSeq, capability)) {
callbackInfo.update(uid, procState, procStateSeq, capability);
if (!callbackInfo.isPending) {
- mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
+ mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, uid, 0)
.sendToTarget();
callbackInfo.isPending = true;
}
@@ -1264,7 +1264,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
synchronized (mUidStateCallbackInfos) {
mUidStateCallbackInfos.remove(uid);
}
- // TODO: b/327058756 - Remove any pending UID_MSG_STATE_CHANGED on the handler.
mUidEventHandler.obtainMessage(UID_MSG_GONE, uid, 0).sendToTarget();
}
};
@@ -5870,13 +5869,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final Handler.Callback mUidEventHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
+ final int uid = msg.arg1;
switch (msg.what) {
case UID_MSG_STATE_CHANGED: {
- handleUidChanged((UidStateCallbackInfo) msg.obj);
+ handleUidChanged(uid);
return true;
}
case UID_MSG_GONE: {
- final int uid = msg.arg1;
handleUidGone(uid);
return true;
}
@@ -5887,23 +5886,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
};
- void handleUidChanged(@NonNull UidStateCallbackInfo uidStateCallbackInfo) {
+ void handleUidChanged(int uid) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
try {
- boolean updated;
- final int uid;
final int procState;
final long procStateSeq;
final int capability;
- synchronized (mUidRulesFirstLock) {
- synchronized (mUidStateCallbackInfos) {
- uid = uidStateCallbackInfo.uid;
- procState = uidStateCallbackInfo.procState;
- procStateSeq = uidStateCallbackInfo.procStateSeq;
- capability = uidStateCallbackInfo.capability;
- uidStateCallbackInfo.isPending = false;
+ synchronized (mUidStateCallbackInfos) {
+ final UidStateCallbackInfo uidStateCallbackInfo = mUidStateCallbackInfos.get(uid);
+ if (uidStateCallbackInfo == null) {
+ // This can happen if UidObserver#onUidGone gets called before we reach
+ // here. In this case, there is no point in processing this change as this
+ // will immediately be followed by a call to handleUidGone anyway.
+ return;
}
-
+ procState = uidStateCallbackInfo.procState;
+ procStateSeq = uidStateCallbackInfo.procStateSeq;
+ capability = uidStateCallbackInfo.capability;
+ uidStateCallbackInfo.isPending = false;
+ }
+ final boolean updated;
+ synchronized (mUidRulesFirstLock) {
// We received a uid state change callback, add it to the history so that it
// will be useful for debugging.
mLogger.uidStateChanged(uid, procState, procStateSeq, capability);
@@ -5926,6 +5929,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
void handleUidGone(int uid) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidGone");
try {
+ synchronized (mUidStateCallbackInfos) {
+ if (mUidStateCallbackInfos.contains(uid)) {
+ // This can happen if UidObserver#onUidStateChanged gets called before we
+ // reach here. In this case, there is no point in processing this change as this
+ // will immediately be followed by a call to handleUidChanged anyway.
+ return;
+ }
+ }
final boolean updated;
synchronized (mUidRulesFirstLock) {
updated = removeUidStateUL(uid);
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index e0376ed6461b..8db59055c1f4 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -28,5 +28,10 @@
}
]
}
+ ],
+ "postsubmit":[
+ {
+ "name":"FrameworksVpnTests"
+ }
]
}
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index ab650afe68a7..27b85746fec2 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -65,7 +65,9 @@ class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
mPowerManager = context.getSystemService(PowerManager.class);
mUiModeManager = context.getSystemService(UiModeManager.class);
- mWallpaperManager = context.getSystemService(WallpaperManager.class);
+ WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
+ mWallpaperManager = wallpaperManager != null && wallpaperManager.isWallpaperSupported()
+ ? wallpaperManager : null;
}
@Override
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index f645eaa28632..3ecc58e2aef2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -942,6 +942,23 @@ abstract public class ManagedServices {
return false;
}
+ protected boolean isPackageOrComponentAllowedWithPermission(ComponentName component,
+ int userId) {
+ if (!(isPackageOrComponentAllowed(component.flattenToString(), userId)
+ || isPackageOrComponentAllowed(component.getPackageName(), userId))) {
+ return false;
+ }
+ return componentHasBindPermission(component, userId);
+ }
+
+ private boolean componentHasBindPermission(ComponentName component, int userId) {
+ ServiceInfo info = getServiceInfo(component, userId);
+ if (info == null) {
+ return false;
+ }
+ return mConfig.bindPermission.equals(info.permission);
+ }
+
boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) {
synchronized (mApproved) {
ArraySet<String> services = mUserSetServices.get(userId);
@@ -1003,6 +1020,7 @@ abstract public class ManagedServices {
for (int uid : uidList) {
if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
anyServicesInvolved = true;
+ trimApprovedListsForInvalidServices(pkgName, UserHandle.getUserId(uid));
}
}
}
@@ -1135,8 +1153,7 @@ abstract public class ManagedServices {
synchronized (mMutex) {
if (enabled) {
- if (isPackageOrComponentAllowed(component.flattenToString(), userId)
- || isPackageOrComponentAllowed(component.getPackageName(), userId)) {
+ if (isPackageOrComponentAllowedWithPermission(component, userId)) {
registerServiceLocked(component, userId);
} else {
Slog.d(TAG, component + " no longer has permission to be bound");
@@ -1270,6 +1287,33 @@ abstract public class ManagedServices {
return removed;
}
+ private void trimApprovedListsForInvalidServices(String packageName, int userId) {
+ synchronized (mApproved) {
+ final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
+ if (approvedByType == null) {
+ return;
+ }
+ for (int i = 0; i < approvedByType.size(); i++) {
+ final ArraySet<String> approved = approvedByType.valueAt(i);
+ for (int j = approved.size() - 1; j >= 0; j--) {
+ final String approvedPackageOrComponent = approved.valueAt(j);
+ if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
+ final ComponentName component = ComponentName.unflattenFromString(
+ approvedPackageOrComponent);
+ if (component != null && !componentHasBindPermission(component, userId)) {
+ approved.removeAt(j);
+ if (DEBUG) {
+ Slog.v(TAG, "Removing " + approvedPackageOrComponent
+ + " from approved list; no bind permission found "
+ + mConfig.bindPermission);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
protected String getPackageName(String packageOrComponent) {
final ComponentName component = ComponentName.unflattenFromString(packageOrComponent);
if (component != null) {
@@ -1519,8 +1563,7 @@ abstract public class ManagedServices {
void reregisterService(final ComponentName cn, final int userId) {
// If rebinding a package that died, ensure it still has permission
// after the rebind delay
- if (isPackageOrComponentAllowed(cn.getPackageName(), userId)
- || isPackageOrComponentAllowed(cn.flattenToString(), userId)) {
+ if (isPackageOrComponentAllowedWithPermission(cn, userId)) {
registerService(cn, userId);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3a7ac0bd659d..ba5882cc7e98 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -12050,11 +12050,12 @@ public class NotificationManagerService extends SystemService {
@Override
public void onServiceAdded(ManagedServiceInfo info) {
if (lifetimeExtensionRefactor()) {
- // Generally, only System or System UI should have the permissions to call
- // registerSystemService.
- // isCallerSystemorPhone tells us whether the caller is System. Then, if it's not
- // the system, we know it's system UI.
- info.isSystemUi = !isCallerSystemOrPhone();
+ // We explicitly check the status bar permission for the uid in the info object.
+ // We can't use the calling uid here because it's probably always system server.
+ // Note that this will also be true for the shell.
+ info.isSystemUi = getContext().checkPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE, -1, info.uid)
+ == PERMISSION_GRANTED;
}
final INotificationListener listener = (INotificationListener) info.service;
final NotificationRankingUpdate update;
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index b9a267f37ff9..8e37b4f24880 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -32,6 +32,7 @@ import android.app.NotificationManager;
import android.content.pm.PackageManager;
import android.os.Process;
import android.service.notification.DNDPolicyProto;
+import android.service.notification.ZenAdapters;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
import android.service.notification.ZenModeConfig.ZenRule;
@@ -591,9 +592,11 @@ class ZenModeEventLogger {
// This applies to both call and message senders, but not conversation senders,
// where they use the same enum values.
proto.write(DNDPolicyProto.ALLOW_CALLS_FROM,
- ZenModeConfig.getZenPolicySenders(mNewPolicy.allowCallsFrom()));
+ ZenAdapters.notificationPolicySendersToZenPolicyPeopleType(
+ mNewPolicy.allowCallsFrom()));
proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM,
- ZenModeConfig.getZenPolicySenders(mNewPolicy.allowMessagesFrom()));
+ ZenAdapters.notificationPolicySendersToZenPolicyPeopleType(
+ mNewPolicy.allowMessagesFrom()));
proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
mNewPolicy.allowConversationsFrom());
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 6857869e3776..bc86c8216952 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -84,6 +84,7 @@ import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.DeviceEffectsApplier;
+import android.service.notification.ZenAdapters;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 15cfca5ca553..1b22154c10f6 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -51,8 +51,15 @@ class IdmapDaemon {
// before stopping the service.
private static final int SERVICE_TIMEOUT_MS = 10000;
- // The amount of time in milliseconds to wait when attempting to connect to idmap service.
- private static final int SERVICE_CONNECT_TIMEOUT_MS = 5000;
+ // The device may enter CPU sleep while waiting for the service startup, and in that mode
+ // the uptime doesn't increment. Thus, we need to have two timeouts: a smaller one for the
+ // uptime and a longer one for the wall time in case when the device never advances the uptime,
+ // so the watchdog won't get triggered.
+
+ // The amount of uptime in milliseconds to wait when attempting to connect to idmap service.
+ private static final int SERVICE_CONNECT_UPTIME_TIMEOUT_MS = 5000;
+ // The amount of wall time in milliseconds to wait.
+ private static final int SERVICE_CONNECT_WALLTIME_TIMEOUT_MS = 30000;
private static final int SERVICE_CONNECT_INTERVAL_SLEEP_MS = 5;
private static final String IDMAP_DAEMON = "idmap2d";
@@ -274,20 +281,29 @@ class IdmapDaemon {
}
}
- final long endMillis = SystemClock.uptimeMillis() + SERVICE_CONNECT_TIMEOUT_MS;
+ long uptimeMillis = SystemClock.uptimeMillis();
+ final long endUptimeMillis = uptimeMillis + SERVICE_CONNECT_UPTIME_TIMEOUT_MS;
+ long walltimeMillis = SystemClock.elapsedRealtime();
+ final long endWalltimeMillis = walltimeMillis + SERVICE_CONNECT_WALLTIME_TIMEOUT_MS;
+
do {
final IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
if (binder != null) {
binder.linkToDeath(
- () -> Slog.w(TAG, String.format("service '%s' died", IDMAP_SERVICE)), 0);
+ () -> Slog.w(TAG,
+ TextUtils.formatSimple("service '%s' died", IDMAP_SERVICE)), 0);
return binder;
}
SystemClock.sleep(SERVICE_CONNECT_INTERVAL_SLEEP_MS);
- } while (SystemClock.uptimeMillis() <= endMillis);
+ } while ((uptimeMillis = SystemClock.uptimeMillis()) <= endUptimeMillis
+ && (walltimeMillis = SystemClock.elapsedRealtime()) <= endWalltimeMillis);
throw new TimeoutException(
- String.format("Failed to connect to '%s' in %d milliseconds", IDMAP_SERVICE,
- SERVICE_CONNECT_TIMEOUT_MS));
+ TextUtils.formatSimple("Failed to connect to '%s' in %d/%d ms (spent %d/%d ms)",
+ IDMAP_SERVICE, SERVICE_CONNECT_UPTIME_TIMEOUT_MS,
+ SERVICE_CONNECT_WALLTIME_TIMEOUT_MS,
+ uptimeMillis - endUptimeMillis + SERVICE_CONNECT_UPTIME_TIMEOUT_MS,
+ walltimeMillis - endWalltimeMillis + SERVICE_CONNECT_WALLTIME_TIMEOUT_MS));
}
private static void stopIdmapService() {
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index a4c4347b7cef..0abe50f12772 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.ondeviceintelligence;
+import static android.service.ondeviceintelligence.OnDeviceIntelligenceService.OnDeviceUpdateProcessingException.PROCESSING_UPDATE_STATUS_CONNECTION_FAILED;
+
import android.Manifest;
import android.annotation.NonNull;
import android.app.AppGlobals;
@@ -36,6 +38,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Bundle;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -43,8 +46,12 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService;
+import android.service.ondeviceintelligence.IRemoteProcessingService;
import android.service.ondeviceintelligence.IRemoteStorageService;
+import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
import android.text.TextUtils;
import android.util.Slog;
@@ -59,10 +66,11 @@ import java.util.Objects;
import java.util.Set;
/**
- * This is the system service for handling calls on the {@link OnDeviceIntelligenceManager}. This
+ * This is the system service for handling calls on the
+ * {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}. This
* service holds connection references to the underlying remote services i.e. the isolated service
- * {@link android.service.ondeviceintelligence.OnDeviceTrustedInferenceService} and a regular
- * service counter part {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService}.
+ * {@link OnDeviceTrustedInferenceService} and a regular
+ * service counter part {@link OnDeviceIntelligenceService}.
*
* Note: Both the remote services run under the SYSTEM user, as we cannot have separate instance of
* the Inference service for each user, due to possible high memory footprint.
@@ -313,10 +321,48 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext,
ComponentName.unflattenFromString(serviceName),
UserHandle.SYSTEM.getIdentifier());
+ mRemoteOnDeviceIntelligenceService.setServiceLifecycleCallbacks(
+ new ServiceConnector.ServiceLifecycleCallbacks<>() {
+ @Override
+ public void onConnected(
+ @NonNull IOnDeviceIntelligenceService service) {
+ try {
+ service.registerRemoteServices(
+ getRemoteProcessingService());
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to send connected event", ex);
+ }
+ }
+ });
}
}
}
+ @NonNull
+ private IRemoteProcessingService.Stub getRemoteProcessingService() {
+ return new IRemoteProcessingService.Stub() {
+ @Override
+ public void updateProcessingState(
+ Bundle processingState,
+ IProcessingUpdateStatusCallback callback) {
+ try {
+ ensureRemoteTrustedInferenceServiceInitialized();
+ mRemoteInferenceService.post(
+ service -> service.updateProcessingState(
+ processingState, callback));
+ } catch (RemoteException unused) {
+ try {
+ callback.onFailure(
+ PROCESSING_UPDATE_STATUS_CONNECTION_FAILED,
+ "Received failure invoking the remote processing service.");
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to send failure status.", ex);
+ }
+ }
+ }
+ };
+ }
+
private void ensureRemoteTrustedInferenceServiceInitialized() throws RemoteException {
synchronized (mLock) {
if (mRemoteInferenceService == null) {
@@ -332,6 +378,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
public void onConnected(
@NonNull IOnDeviceTrustedInferenceService service) {
try {
+ ensureRemoteIntelligenceServiceInitialized();
service.registerRemoteStorageService(
getIRemoteStorageService());
} catch (RemoteException ex) {
@@ -358,8 +405,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
@Override
public void getReadOnlyFeatureFileDescriptorMap(
Feature feature,
- RemoteCallback remoteCallback)
- throws RemoteException {
+ RemoteCallback remoteCallback) {
mRemoteOnDeviceIntelligenceService.post(
service -> service.getReadOnlyFeatureFileDescriptorMap(
feature, remoteCallback));
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 8452c0e61a81..4eb8b2b980cb 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -21,7 +21,6 @@ import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.app.role.RoleManager;
@@ -39,6 +38,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -96,6 +96,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
private final Object mLock = new Object();
+ private final Injector mInjector;
private final Context mContext;
private final AppOpsManager mAppOps;
private final TelephonyManager mTelephonyManager;
@@ -345,6 +346,18 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
AtomicFile getMappingFile() {
return mMappingFile;
}
+
+ UserManager getUserManager() {
+ return mContext.getSystemService(UserManager.class);
+ }
+
+ DevicePolicyManager getDevicePolicyManager() {
+ return mContext.getSystemService(DevicePolicyManager.class);
+ }
+
+ void setSystemProperty(String key, String value) {
+ SystemProperties.set(key, value);
+ }
}
BugreportManagerServiceImpl(Context context) {
@@ -356,6 +369,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
BugreportManagerServiceImpl(Injector injector) {
+ mInjector = injector;
mContext = injector.getContext();
mAppOps = mContext.getSystemService(AppOpsManager.class);
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
@@ -388,12 +402,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
int callingUid = Binder.getCallingUid();
enforcePermission(callingPackage, callingUid, bugreportMode
== BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */);
- final long identity = Binder.clearCallingIdentity();
- try {
- ensureUserCanTakeBugReport(bugreportMode);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ ensureUserCanTakeBugReport(bugreportMode);
Slogf.i(TAG, "Starting bugreport for %s / %d", callingPackage, callingUid);
synchronized (mLock) {
@@ -432,7 +441,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@RequiresPermission(value = Manifest.permission.DUMP, conditional = true)
public void retrieveBugreport(int callingUidUnused, String callingPackage, int userId,
FileDescriptor bugreportFd, String bugreportFile,
-
boolean keepBugreportOnRetrievalUnused, IDumpstateListener listener) {
int callingUid = Binder.getCallingUid();
enforcePermission(callingPackage, callingUid, false);
@@ -564,54 +572,59 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
/**
- * Validates that the current user is an admin user or, when bugreport is requested remotely
- * that the current user is an affiliated user.
+ * Validates that the calling user is an admin user or, when bugreport is requested remotely
+ * that the user is an affiliated user.
*
- * @throws IllegalArgumentException if the current user is not an admin user
+ * @throws IllegalArgumentException if the calling user or the parent of the calling profile
+ * user is not an admin user.
*/
private void ensureUserCanTakeBugReport(int bugreportMode) {
- UserInfo currentUser = null;
+ // Get the calling userId before clearing the caller identity.
+ int effectiveCallingUserId = UserHandle.getUserId(Binder.getCallingUid());
+ boolean isAdminUser = false;
+ final long identity = Binder.clearCallingIdentity();
try {
- currentUser = ActivityManager.getService().getCurrentUser();
- } catch (RemoteException e) {
- // Impossible to get RemoteException for an in-process call.
- }
-
- if (currentUser == null) {
- logAndThrow("There is no current user, so no bugreport can be requested.");
+ UserInfo profileParent =
+ mInjector.getUserManager().getProfileParent(effectiveCallingUserId);
+ if (profileParent == null) {
+ isAdminUser = mInjector.getUserManager().isUserAdmin(effectiveCallingUserId);
+ } else {
+ // If the caller is a profile, we need to check its parent user instead.
+ // Therefore setting the profile parent user as the effective calling user.
+ effectiveCallingUserId = profileParent.id;
+ isAdminUser = profileParent.isAdmin();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
-
- if (!currentUser.isAdmin()) {
+ if (!isAdminUser) {
if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE
- && isCurrentUserAffiliated(currentUser.id)) {
+ && isUserAffiliated(effectiveCallingUserId)) {
return;
}
- logAndThrow(TextUtils.formatSimple("Current user %s is not an admin user."
- + " Only admin users are allowed to take bugreport.", currentUser.id));
+ logAndThrow(TextUtils.formatSimple("Calling user %s is not an admin user."
+ + " Only admin users and their profiles are allowed to take bugreport.",
+ effectiveCallingUserId));
}
}
/**
- * Returns {@code true} if the device has device owner and the current user is affiliated
+ * Returns {@code true} if the device has device owner and the specified user is affiliated
* with the device owner.
*/
- private boolean isCurrentUserAffiliated(int currentUserId) {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ private boolean isUserAffiliated(int userId) {
+ DevicePolicyManager dpm = mInjector.getDevicePolicyManager();
int deviceOwnerUid = dpm.getDeviceOwnerUserId();
if (deviceOwnerUid == UserHandle.USER_NULL) {
return false;
}
- int callingUserId = UserHandle.getUserId(Binder.getCallingUid());
-
- Slog.i(TAG, "callingUid: " + callingUserId + " deviceOwnerUid: " + deviceOwnerUid
- + " currentUserId: " + currentUserId);
-
- if (callingUserId != deviceOwnerUid) {
- logAndThrow("Caller is not device owner on provisioned device.");
+ if (DEBUG) {
+ Slog.d(TAG, "callingUid: " + userId + " deviceOwnerUid: " + deviceOwnerUid);
}
- if (!dpm.isAffiliatedUser(currentUserId)) {
- logAndThrow("Current user is not affiliated to the device owner.");
+
+ if (userId != deviceOwnerUid && !dpm.isAffiliatedUser(userId)) {
+ logAndThrow("User " + userId + " is not affiliated to the device owner.");
}
return true;
}
@@ -728,7 +741,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@GuardedBy("mLock")
private IDumpstate startAndGetDumpstateBinderServiceLocked() {
// Start bugreport service.
- SystemProperties.set("ctl.start", BUGREPORT_SERVICE);
+ mInjector.setSystemProperty("ctl.start", BUGREPORT_SERVICE);
IDumpstate ds = null;
boolean timedOut = false;
@@ -760,7 +773,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
// This tells init to cancel bugreportd service. Note that this is achieved through
// setting a system property which is not thread-safe. So the lock here offers
// thread-safety only among callers of the API.
- SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
+ mInjector.setSystemProperty("ctl.stop", BUGREPORT_SERVICE);
}
@RequiresPermission(android.Manifest.permission.DUMP)
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 9afdde53643c..b5476fdd3050 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -760,13 +760,18 @@ public class ComputerEngine implements Computer {
if (pkgName == null) {
if (!mCrossProfileIntentResolverEngine.shouldSkipCurrentProfile(this, intent,
resolvedType, userId)) {
- /*
- Check for results in the current profile only if there is no
- {@link CrossProfileIntentFilter} for user with flag
- {@link PackageManager.SKIP_CURRENT_PROFILE} set.
- */
- result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this,
- intent, resolvedType, flags, userId), userId));
+
+ final List<ResolveInfo> queryResult = mComponentResolver.queryActivities(this,
+ intent, resolvedType, flags, userId);
+ // If the user doesn't exist, the queryResult is null
+ if (queryResult != null) {
+ /*
+ Check for results in the current profile only if there is no
+ {@link CrossProfileIntentFilter} for user with flag
+ {@link PackageManager.SKIP_CURRENT_PROFILE} set.
+ */
+ result.addAll(filterIfNotSystemUser(queryResult, userId));
+ }
}
addInstant = isInstantAppResolutionAllowed(intent, result, userId,
false /*skipPackageCheck*/, flags);
@@ -788,9 +793,13 @@ public class ComputerEngine implements Computer {
if (setting != null && setting.getAndroidPackage() != null && (resolveForStart
|| !shouldFilterApplication(setting, filterCallingUid, userId))) {
- result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this,
+ final List<ResolveInfo> queryResult = mComponentResolver.queryActivities(this,
intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
- userId), userId));
+ userId);
+ // If the user doesn't exist, the queryResult is null
+ if (queryResult != null) {
+ result.addAll(filterIfNotSystemUser(queryResult, userId));
+ }
}
if (result == null || result.size() == 0) {
// the caller wants to resolve for a particular package; however, there
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 5bd8ca63c3ff..4bfd077760e4 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1080,7 +1080,7 @@ final class InstallPackageHelper {
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
requests, Collections.unmodifiableMap(mPm.mPackages),
versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
- mPm.mSettings);
+ mPm.mSettings, mPm.mInjector.getSystemConfig());
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.setError("Reconciliation failed...", e);
@@ -1170,6 +1170,7 @@ final class InstallPackageHelper {
@GuardedBy("mPm.mInstallLock")
private void preparePackageLI(InstallRequest request) throws PrepareFailure {
+ final int[] allUsers = mPm.mUserManager.getUserIds();
final int installFlags = request.getInstallFlags();
final boolean onExternal = request.getVolumeUuid() != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
@@ -1445,7 +1446,7 @@ final class InstallPackageHelper {
systemApp = ps.isSystem();
request.setOriginUsers(ps.queryUsersInstalledOrHasData(
- mPm.mUserManager.getUserIds()));
+ allUsers));
}
final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
@@ -1703,7 +1704,6 @@ final class InstallPackageHelper {
final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final int[] allUsers;
final int[] installedUsers;
final int[] uninstalledUsers;
@@ -1796,7 +1796,6 @@ final class InstallPackageHelper {
}
// In case of rollback, remember per-user/profile install state
- allUsers = mPm.mUserManager.getUserIds();
installedUsers = ps.queryInstalledUsers(allUsers, true);
uninstalledUsers = ps.queryInstalledUsers(allUsers, false);
@@ -2220,7 +2219,7 @@ final class InstallPackageHelper {
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
if (ps != null) {
installRequest.setNewUsers(
- ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true));
+ ps.queryInstalledUsers(allUsers, true));
ps.setUpdateAvailable(false /*updateAvailable*/);
File appMetadataFile = new File(ps.getPath(), APP_METADATA_FILE_NAME);
@@ -2327,7 +2326,7 @@ final class InstallPackageHelper {
// Retrieve the overlays for shared libraries of the package.
if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
for (SharedLibraryWrapper sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
- for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+ for (int currentUserId : allUsers) {
if (sharedLib.getType() != SharedLibraryInfo.TYPE_DYNAMIC) {
// TODO(146804378): Support overlaying static shared libraries
continue;
@@ -2416,9 +2415,8 @@ final class InstallPackageHelper {
}
// Set install reason for users that are having the package newly installed.
- final int[] allUsersList = mPm.mUserManager.getUserIds();
if (userId == UserHandle.USER_ALL) {
- for (int currentUserId : allUsersList) {
+ for (int currentUserId : allUsers) {
if (!previousUserIds.contains(currentUserId)
&& ps.getInstalled(currentUserId)) {
ps.setInstallReason(installReason, currentUserId);
@@ -2438,7 +2436,7 @@ final class InstallPackageHelper {
}
// Ensure that the uninstall reason is UNKNOWN for users with the package installed.
- for (int currentUserId : allUsersList) {
+ for (int currentUserId : allUsers) {
if (ps.getInstalled(currentUserId)) {
ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
}
@@ -3812,7 +3810,7 @@ final class InstallPackageHelper {
mPm.mPackages, Collections.singletonMap(pkgName,
mPm.getSettingsVersionForPackage(parsedPackage)),
mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
- mPm.mSettings);
+ mPm.mSettings, mPm.mInjector.getSystemConfig());
if ((scanFlags & SCAN_AS_APEX) == 0) {
appIdCreated = optimisticallyRegisterAppId(installRequest);
} else {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9b0fec2c757b..6b56b85938c2 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -161,6 +161,9 @@ import java.util.zip.ZipOutputStream;
public class LauncherAppsService extends SystemService {
private static final String WM_TRACE_DIR = "/data/misc/wmtrace/";
private static final String VC_FILE_SUFFIX = ".vc";
+ // TODO(b/310027945): Update the intent name.
+ private static final String PS_SETTINGS_INTENT =
+ "com.android.settings.action.PRIVATE_SPACE_SETUP_FLOW";
private static final Set<PosixFilePermission> WM_TRACE_FILE_PERMISSIONS = Set.of(
PosixFilePermission.OWNER_WRITE,
@@ -1777,6 +1780,27 @@ public class LauncherAppsService extends SystemService {
}
}
+ @Override
+ public @Nullable IntentSender getPrivateSpaceSettingsIntent() {
+ if (!canAccessHiddenProfile(getCallingUid(), getCallingPid())) {
+ Slog.e(TAG, "Caller cannot access hidden profiles");
+ return null;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Intent psSettingsIntent = new Intent(PS_SETTINGS_INTENT);
+ psSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ final PendingIntent pi = PendingIntent.getActivity(mContext,
+ /* requestCode */ 0,
+ psSettingsIntent,
+ PendingIntent.FLAG_IMMUTABLE | FLAG_UPDATE_CURRENT);
+ return pi == null ? null : pi.getIntentSender();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Nullable
private IntentSender buildAppMarketIntentSenderForUser(@NonNull UserHandle user) {
Intent appMarketIntent = new Intent(Intent.ACTION_MAIN);
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index ef8453dcee67..29de26e41df4 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -31,6 +31,7 @@ import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
import static android.content.pm.PackageManager.DELETE_ALL_USERS;
import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
+import static android.content.pm.PackageManager.INSTALL_UNARCHIVE;
import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction;
import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
@@ -754,8 +755,9 @@ public class PackageArchiver {
int draftSessionId;
try {
- draftSessionId = Binder.withCleanCallingIdentity(() ->
- createDraftSession(packageName, installerPackage, statusReceiver, userId));
+ draftSessionId = Binder.withCleanCallingIdentity(
+ () -> createDraftSession(packageName, installerPackage, callerPackageName,
+ statusReceiver, userId));
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw ExceptionUtils.wrap((IOException) e.getCause());
@@ -795,11 +797,18 @@ public class PackageArchiver {
}
private int createDraftSession(String packageName, String installerPackage,
+ String callerPackageName,
IntentSender statusReceiver, int userId) throws IOException {
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
sessionParams.setAppPackageName(packageName);
- sessionParams.installFlags = INSTALL_UNARCHIVE_DRAFT;
+ sessionParams.setAppLabel(
+ mContext.getString(com.android.internal.R.string.unarchival_session_app_label));
+ sessionParams.setAppIcon(
+ getArchivedAppIcon(packageName, UserHandle.of(userId), callerPackageName));
+ // To make sure SessionInfo::isUnarchival returns true for draft sessions,
+ // INSTALL_UNARCHIVE is also set.
+ sessionParams.installFlags = (INSTALL_UNARCHIVE_DRAFT | INSTALL_UNARCHIVE);
int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId);
// Handles case of repeated unarchival calls for the same package.
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 9a7916a7b215..90d6adc4fa52 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
@@ -25,6 +26,7 @@ import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
import static com.android.server.pm.PackageManagerService.TAG;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
@@ -36,6 +38,7 @@ import android.util.Slog;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.SystemConfig;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.utils.WatchedLongSparseArray;
@@ -53,14 +56,17 @@ import java.util.Map;
* as install) led to the request.
*/
final class ReconcilePackageUtils {
- private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE || true;
+ // TODO(b/308573259): with allow-list, we should be able to disallow such installs even in
+ // debuggable builds.
+ private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SHAREDUIDS = Build.IS_DEBUGGABLE
+ || !Flags.restrictNonpreloadsSystemShareduids();
public static List<ReconciledPackage> reconcilePackages(
List<InstallRequest> installRequests,
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos,
SharedLibrariesImpl sharedLibraries,
- KeySetManagerService ksms, Settings settings)
+ KeySetManagerService ksms, Settings settings, SystemConfig systemConfig)
throws ReconcileFailure {
final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());
@@ -187,11 +193,19 @@ final class ReconcilePackageUtils {
SigningDetails.CertCapabilities.PERMISSION)) {
Slog.d(TAG, "Non-preload app associated with system signature: "
+ signatureCheckPs.getPackageName());
- if (!ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE) {
- throw new ReconcileFailure(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Non-preload app associated with system signature: "
- + signatureCheckPs.getPackageName());
+ if (sharedUserSetting != null && !ALLOW_NON_PRELOADS_SYSTEM_SHAREDUIDS) {
+ // Check the allow-list.
+ var allowList = systemConfig.getPackageToSharedUidAllowList();
+ var sharedUidName = allowList.get(signatureCheckPs.getPackageName());
+ if (sharedUidName == null
+ || !sharedUserSetting.name.equals(sharedUidName)) {
+ var msg = "Non-preload app " + signatureCheckPs.getPackageName()
+ + " signed with platform signature and joining shared uid: "
+ + sharedUserSetting.name;
+ Slog.e(TAG, msg + ", allowList: " + allowList);
+ throw new ReconcileFailure(
+ INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, msg);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fe65010b7281..59d621959ac5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3386,12 +3386,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
} else if (tagName.equals("verifier")) {
final String deviceIdentity = parser.getAttributeValue(null, "device");
- try {
- mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity);
- } catch (IllegalArgumentException e) {
- Slog.w(PackageManagerService.TAG, "Discard invalid verifier device id: "
- + e.getMessage());
- }
+ mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity);
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
// No longer used.
} else if (tagName.equals("keyset-settings")) {
@@ -3419,7 +3414,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
str.close();
- } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) {
+ } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException
+ | IllegalArgumentException e) {
// Remove corrupted file and retry.
atomicFile.failRead(str, e);
@@ -4558,6 +4554,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
for (int i = 0; i < size; i++) {
final PackageSetting ps = mPackages.valueAt(i);
if (ps.getPkg() == null) {
+ // This would force-create correct per-user state.
+ ps.setInstalled(false, userHandle);
+ // Make sure the app is excluded from storage mapping for this user.
+ writeKernelMappingLPr(ps);
continue;
}
final boolean shouldMaybeInstall = ps.isSystem() &&
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a600eeabf62b..211b7546bd8a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -520,7 +520,7 @@ public class ShortcutService extends IShortcutService.Stub {
mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
mShortcutDumpFiles = new ShortcutDumpFiles(this);
mIsAppSearchEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, true)
+ SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, false)
&& !injectIsLowRamDevice();
if (onlyForPackageManagerApis) {
@@ -3795,6 +3795,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ final boolean archival = intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false);
switch (action) {
case Intent.ACTION_PACKAGE_ADDED:
@@ -3805,7 +3806,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
break;
case Intent.ACTION_PACKAGE_REMOVED:
- if (!replacing) {
+ if (!replacing || (replacing && archival)) {
handlePackageRemoved(packageName, userId);
}
break;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 47032ea2d6af..754b141ff10d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -408,6 +408,9 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
/**
* Gets the permission states for requested package, persistent device and user.
+ * <p>
+ * <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the
+ * exact permission states for the requested device.
*
* @param packageName name of the package you are checking against
* @param deviceId id of the persistent device you are checking against
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 539bb6b22ba2..3a79d0df4819 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -17,6 +17,7 @@
package com.android.server.policy;
import android.annotation.SuppressLint;
+import android.app.role.RoleManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -45,6 +46,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
/**
* Manages quick launch shortcuts by:
@@ -52,8 +55,8 @@ import java.io.IOException;
* <li> Returning a shortcut-matching intent to clients
* <li> Returning particular kind of application intent by special key.
*/
-class ModifierShortcutManager {
- private static final String TAG = "WindowManager";
+public class ModifierShortcutManager {
+ private static final String TAG = "ModifierShortcutManager";
private static final String TAG_BOOKMARKS = "bookmarks";
private static final String TAG_BOOKMARK = "bookmark";
@@ -63,9 +66,13 @@ class ModifierShortcutManager {
private static final String ATTRIBUTE_SHORTCUT = "shortcut";
private static final String ATTRIBUTE_CATEGORY = "category";
private static final String ATTRIBUTE_SHIFT = "shift";
+ private static final String ATTRIBUTE_ROLE = "role";
private final SparseArray<Intent> mIntentShortcuts = new SparseArray<>();
private final SparseArray<Intent> mShiftShortcuts = new SparseArray<>();
+ private final SparseArray<String> mRoleShortcuts = new SparseArray<String>();
+ private final SparseArray<String> mShiftRoleShortcuts = new SparseArray<String>();
+ private final Map<String, Intent> mRoleIntents = new HashMap<String, Intent>();
private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
@@ -76,10 +83,12 @@ class ModifierShortcutManager {
* usage page. We don't support quite that many yet...
*/
static SparseArray<String> sApplicationLaunchKeyCategories;
+ static SparseArray<String> sApplicationLaunchKeyRoles;
static {
+ sApplicationLaunchKeyRoles = new SparseArray<String>();
sApplicationLaunchKeyCategories = new SparseArray<String>();
- sApplicationLaunchKeyCategories.append(
- KeyEvent.KEYCODE_EXPLORER, Intent.CATEGORY_APP_BROWSER);
+ sApplicationLaunchKeyRoles.append(
+ KeyEvent.KEYCODE_EXPLORER, RoleManager.ROLE_BROWSER);
sApplicationLaunchKeyCategories.append(
KeyEvent.KEYCODE_ENVELOPE, Intent.CATEGORY_APP_EMAIL);
sApplicationLaunchKeyCategories.append(
@@ -92,14 +101,25 @@ class ModifierShortcutManager {
KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR);
}
+ public static final String EXTRA_ROLE =
+ "com.android.server.policy.ModifierShortcutManager.EXTRA_ROLE";
+
private final Context mContext;
private final Handler mHandler;
+ private final RoleManager mRoleManager;
+ private final PackageManager mPackageManager;
private boolean mSearchKeyShortcutPending = false;
private boolean mConsumeSearchKeyUp = true;
ModifierShortcutManager(Context context, Handler handler) {
mContext = context;
mHandler = handler;
+ mPackageManager = mContext.getPackageManager();
+ mRoleManager = mContext.getSystemService(RoleManager.class);
+ mRoleManager.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
+ (String roleName, UserHandle user) -> {
+ mRoleIntents.remove(roleName);
+ }, UserHandle.ALL);
loadShortcuts();
}
@@ -141,14 +161,42 @@ class ModifierShortcutManager {
shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
if (shortcutChar != 0) {
shortcutIntent = shortcutMap.get(shortcutChar);
+
+ if (shortcutIntent == null) {
+ // Check for role based shortcut
+ String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
+ : mRoleShortcuts.get(shortcutChar);
+ if (role != null) {
+ shortcutIntent = getRoleLaunchIntent(role);
+ }
+ }
}
}
return shortcutIntent;
}
+ private Intent getRoleLaunchIntent(String role) {
+ Intent intent = mRoleIntents.get(role);
+ if (intent == null) {
+ if (mRoleManager.isRoleAvailable(role)) {
+ String rolePackage = mRoleManager.getDefaultApplication(role);
+ if (rolePackage != null) {
+ intent = mPackageManager.getLaunchIntentForPackage(rolePackage);
+ intent.putExtra(EXTRA_ROLE, role);
+ mRoleIntents.put(role, intent);
+ } else {
+ Log.w(TAG, "No default application for role " + role);
+ }
+ } else {
+ Log.w(TAG, "Role " + role + " is not available.");
+ }
+ }
+ return intent;
+ }
+
private void loadShortcuts() {
- PackageManager packageManager = mContext.getPackageManager();
+
try {
XmlResourceParser parser = mContext.getResources().getXml(
com.android.internal.R.xml.bookmarks);
@@ -170,29 +218,37 @@ class ModifierShortcutManager {
String shortcutName = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT);
String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY);
String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT);
+ String roleName = parser.getAttributeValue(null, ATTRIBUTE_ROLE);
if (TextUtils.isEmpty(shortcutName)) {
- Log.w(TAG, "Unable to get shortcut for: " + packageName + "/" + className);
+ Log.w(TAG, "Shortcut required for bookmark with category=" + categoryName
+ + " packageName=" + packageName + " className=" + className
+ + " role=" + roleName + "shiftName=" + shiftName);
continue;
}
final int shortcutChar = shortcutName.charAt(0);
final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true"));
-
final Intent intent;
if (packageName != null && className != null) {
+ if (roleName != null || categoryName != null) {
+ Log.w(TAG, "Cannot specify role or category when package and class"
+ + " are present for bookmark packageName=" + packageName
+ + " className=" + className + " shortcutChar=" + shortcutChar);
+ continue;
+ }
ComponentName componentName = new ComponentName(packageName, className);
try {
- packageManager.getActivityInfo(componentName,
+ mPackageManager.getActivityInfo(componentName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
- String[] packages = packageManager.canonicalToCurrentPackageNames(
+ String[] packages = mPackageManager.canonicalToCurrentPackageNames(
new String[] { packageName });
componentName = new ComponentName(packages[0], className);
try {
- packageManager.getActivityInfo(componentName,
+ mPackageManager.getActivityInfo(componentName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES);
@@ -207,10 +263,25 @@ class ModifierShortcutManager {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(componentName);
} else if (categoryName != null) {
+ if (roleName != null) {
+ Log.w(TAG, "Cannot specify role bookmark when category is present for"
+ + " bookmark shortcutChar=" + shortcutChar
+ + " category= " + categoryName);
+ continue;
+ }
intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName);
+ } else if (roleName != null) {
+ // We can't resolve the role at the time of this file being parsed as the
+ // device hasn't finished booting, so we will look it up lazily.
+ if (isShiftShortcut) {
+ mShiftRoleShortcuts.put(shortcutChar, roleName);
+ } else {
+ mRoleShortcuts.put(shortcutChar, roleName);
+ }
+ continue;
} else {
Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName
- + ": missing package/class or category attributes");
+ + ": missing package/class, category or role attributes");
continue;
}
@@ -298,10 +369,17 @@ class ModifierShortcutManager {
// Invoke shortcuts using Meta.
metaState &= ~KeyEvent.META_META_MASK;
} else {
+ Intent intent = null;
// Handle application launch keys.
+ String role = sApplicationLaunchKeyRoles.get(keyCode);
String category = sApplicationLaunchKeyCategories.get(keyCode);
- if (category != null) {
- Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
+ if (role != null) {
+ intent = getRoleLaunchIntent(role);
+ } else if (category != null) {
+ intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
+ }
+
+ if (intent != null) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
@@ -309,7 +387,7 @@ class ModifierShortcutManager {
Slog.w(TAG, "Dropping application launch key because "
+ "the activity to which it is registered was not found: "
+ "keyCode=" + KeyEvent.keyCodeToString(keyCode) + ","
- + " category=" + category);
+ + " category=" + category + " role=" + role);
}
logKeyboardShortcut(keyEvent, KeyboardLogEvent.getLogEventFromIntent(intent));
return true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 428fca082f75..ec4b38b10af2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3504,7 +3504,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
- statusbar.moveFocusedTaskToFullscreen(event.getDisplayId());
+ statusbar.moveFocusedTaskToFullscreen(getTargetDisplayIdForKeyEvent(event));
logKeyboardSystemsEvent(event, KeyboardLogEvent.MULTI_WINDOW_NAVIGATION);
return true;
}
@@ -3514,7 +3514,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
- statusbar.enterDesktop(event.getDisplayId());
+ statusbar.enterDesktop(getTargetDisplayIdForKeyEvent(event));
logKeyboardSystemsEvent(event, KeyboardLogEvent.DESKTOP_MODE);
return true;
}
@@ -6410,7 +6410,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private boolean performHapticFeedback(int effectId, boolean always, String reason) {
return performHapticFeedback(Process.myUid(), mContext.getOpPackageName(),
- effectId, always, reason);
+ effectId, always, reason, false /* fromIme */);
}
@Override
@@ -6420,7 +6420,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public boolean performHapticFeedback(int uid, String packageName, int effectId,
- boolean always, String reason) {
+ boolean always, String reason, boolean fromIme) {
if (!mVibrator.hasVibrator()) {
return false;
}
@@ -6431,7 +6431,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
VibrationAttributes attrs =
mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ always);
+ effectId, /* bypassVibrationIntensitySetting= */ always, fromIme);
VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, effectId);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
@@ -6951,4 +6951,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
== PERMISSION_GRANTED;
}
}
+
+ private int getTargetDisplayIdForKeyEvent(KeyEvent event) {
+ int displayId = event.getDisplayId();
+
+ if (displayId == INVALID_DISPLAY) {
+ displayId = mTopFocusedDisplayId;
+ }
+
+ if (displayId == INVALID_DISPLAY) {
+ return DEFAULT_DISPLAY;
+ } else {
+ return displayId;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 32a21c587f08..cebf7fb9a627 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -21,7 +21,6 @@ import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
@@ -148,7 +147,7 @@ public abstract class SoftRestrictedPermissionPolicy {
pkg.hasPreserveLegacyExternalStorage();
targetSDK = getMinimumTargetSDK(context, appInfo, user);
- shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ shouldApplyRestriction = !isWhiteListed;
isForcedScopedStorage = sForcedScopedStorageAppWhitelist
.contains(appInfo.packageName);
} else {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 2174fd62ea00..5956594acd26 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1072,7 +1072,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* Call from application to perform haptic feedback on its window.
*/
public boolean performHapticFeedback(int uid, String packageName, int effectId,
- boolean always, String reason);
+ boolean always, String reason, boolean fromIme);
/**
* Called when we have started keeping the screen on because a window
diff --git a/services/core/java/com/android/server/power/Android.bp b/services/core/java/com/android/server/power/Android.bp
index 607d435b5410..863ff76cb0c7 100644
--- a/services/core/java/com/android/server/power/Android.bp
+++ b/services/core/java/com/android/server/power/Android.bp
@@ -9,4 +9,5 @@ aconfig_declarations {
java_aconfig_library {
name: "backstage_power_flags_lib",
aconfig_declarations: "backstage_power_flags",
+ sdk_version: "system_current",
}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index f8c678aa3aa3..52ef87cf949f 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -45,11 +45,9 @@ import static android.hardware.SensorPrivacyManager.Sources.OTHER;
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
import static android.hardware.SensorPrivacyManager.Sources.SHELL;
-import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
-import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
-import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
import static android.hardware.SensorPrivacyManager.StateTypes.DISABLED;
import static android.hardware.SensorPrivacyManager.StateTypes.ENABLED;
+import static android.hardware.SensorPrivacyManager.StateTypes.ENABLED_EXCEPT_ALLOWLISTED_APPS;
import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
import static android.os.UserHandle.USER_NULL;
@@ -57,11 +55,9 @@ import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
-import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
-import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
+import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON_EXCEPT_ALLOWLISTED_APPS;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__MICROPHONE;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__SENSOR_UNKNOWN;
@@ -98,7 +94,6 @@ import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.drawable.Icon;
-import android.hardware.CameraPrivacyAllowlistEntry;
import android.hardware.ISensorPrivacyListener;
import android.hardware.ISensorPrivacyManager;
import android.hardware.SensorPrivacyManager;
@@ -153,7 +148,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -170,18 +164,12 @@ public final class SensorPrivacyService extends SystemService {
public static final int REMINDER_DIALOG_DELAY_MILLIS = 500;
@FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS =
- PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
- @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS =
- PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
- @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
- private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS =
- PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
- @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
private static final int ACTION__TOGGLE_ON =
PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
@FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private static final int ACTION__TOGGLE_ON_EXCEPT_ALLOWLISTED_APPS =
+ PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON_EXCEPT_ALLOWLISTED_APPS;
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
private static final int ACTION__TOGGLE_OFF =
PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
@FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
@@ -208,8 +196,7 @@ public final class SensorPrivacyService extends SystemService {
private CallStateHelper mCallStateHelper;
private KeyguardManager mKeyguardManager;
- List<CameraPrivacyAllowlistEntry> mCameraPrivacyAllowlist =
- new ArrayList<CameraPrivacyAllowlistEntry>();
+ List<String> mCameraPrivacyAllowlist = new ArrayList<String>();
private int mCurrentUser = USER_NULL;
@@ -227,14 +214,8 @@ public final class SensorPrivacyService extends SystemService {
mPackageManagerInternal = getLocalService(PackageManagerInternal.class);
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
- ArrayMap<String, Boolean> cameraPrivacyAllowlist =
- SystemConfig.getInstance().getCameraPrivacyAllowlist();
-
- for (Map.Entry<String, Boolean> entry : cameraPrivacyAllowlist.entrySet()) {
- CameraPrivacyAllowlistEntry ent = new CameraPrivacyAllowlistEntry();
- ent.packageName = entry.getKey();
- ent.isMandatory = entry.getValue();
- mCameraPrivacyAllowlist.add(ent);
+ for (String entry : SystemConfig.getInstance().getCameraPrivacyAllowlist()) {
+ mCameraPrivacyAllowlist.add(entry);
}
}
@@ -908,14 +889,8 @@ public final class SensorPrivacyService extends SystemService {
case DISABLED :
logAction = ACTION__TOGGLE_ON;
break;
- case AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS :
- logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
- break;
- case AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS :
- logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
- break;
- case AUTOMOTIVE_DRIVER_ASSISTANCE_APPS :
- logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
+ case ENABLED_EXCEPT_ALLOWLISTED_APPS :
+ logAction = ACTION__TOGGLE_ON_EXCEPT_ALLOWLISTED_APPS;
break;
default :
logAction = ACTION__ACTION_UNKNOWN;
@@ -981,11 +956,23 @@ public final class SensorPrivacyService extends SystemService {
@Override
@FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist() {
+ public List<String> getCameraPrivacyAllowlist() {
enforceObserveSensorPrivacyPermission();
return mCameraPrivacyAllowlist;
}
+ /**
+ * Sets camera privacy allowlist.
+ * @param allowlist List of automotive driver assistance packages for
+ * privacy allowlisting.
+ * @hide
+ */
+ @Override
+ public void setCameraPrivacyAllowlist(List<String> allowlist) {
+ enforceManageSensorPrivacyPermission();
+ mCameraPrivacyAllowlist = new ArrayList<>(allowlist);
+ }
+
@Override
@FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
@@ -1005,23 +992,9 @@ public final class SensorPrivacyService extends SystemService {
return true;
} else if (state == DISABLED) {
return false;
- } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS) {
- for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) {
- if ((packageName.equals(entry.packageName)) && !entry.isMandatory) {
- return false;
- }
- }
- return true;
- } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS) {
- for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) {
- if ((packageName.equals(entry.packageName)) && entry.isMandatory) {
- return false;
- }
- }
- return true;
- } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_APPS) {
- for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) {
- if (packageName.equals(entry.packageName)) {
+ } else if (state == ENABLED_EXCEPT_ALLOWLISTED_APPS) {
+ for (String entry : mCameraPrivacyAllowlist) {
+ if (packageName.equals(entry)) {
return false;
}
}
@@ -1616,20 +1589,7 @@ public final class SensorPrivacyService extends SystemService {
setToggleSensorPrivacy(userId, SHELL, sensor, false);
}
break;
- case "automotive_driver_assistance_apps" : {
- if (Flags.cameraPrivacyAllowlist()) {
- int sensor = sensorStrToId(getNextArgRequired());
- if ((!isAutomotive(mContext)) || (sensor != CAMERA)) {
- pw.println("Command not valid for this sensor");
- return -1;
- }
-
- setToggleSensorPrivacyState(userId, SHELL, sensor,
- AUTOMOTIVE_DRIVER_ASSISTANCE_APPS);
- }
- }
- break;
- case "automotive_driver_assistance_helpful_apps" : {
+ case "enable_except_allowlisted_apps" : {
if (Flags.cameraPrivacyAllowlist()) {
int sensor = sensorStrToId(getNextArgRequired());
if ((!isAutomotive(mContext)) || (sensor != CAMERA)) {
@@ -1638,20 +1598,7 @@ public final class SensorPrivacyService extends SystemService {
}
setToggleSensorPrivacyState(userId, SHELL, sensor,
- AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS);
- }
- }
- break;
- case "automotive_driver_assistance_required_apps" : {
- if (Flags.cameraPrivacyAllowlist()) {
- int sensor = sensorStrToId(getNextArgRequired());
- if ((!isAutomotive(mContext)) || (sensor != CAMERA)) {
- pw.println("Command not valid for this sensor");
- return -1;
- }
-
- setToggleSensorPrivacyState(userId, SHELL, sensor,
- AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS);
+ ENABLED_EXCEPT_ALLOWLISTED_APPS);
}
}
break;
@@ -1679,18 +1626,9 @@ public final class SensorPrivacyService extends SystemService {
pw.println("");
if (Flags.cameraPrivacyAllowlist()) {
if (isAutomotive(mContext)) {
- pw.println(" automotive_driver_assistance_apps USER_ID SENSOR");
- pw.println(" Disable privacy for automotive apps which help you"
- + " drive and apps which are required by OEM");
- pw.println("");
- pw.println(" automotive_driver_assistance_helpful_apps "
- + "USER_ID SENSOR");
- pw.println(" Disable privacy for automotive apps which "
- + "help you drive.");
- pw.println("");
- pw.println(" automotive_driver_assistance_required_apps "
+ pw.println(" enable_except_allowlisted_apps "
+ "USER_ID SENSOR");
- pw.println(" Disable privacy for automotive apps which are "
+ pw.println(" Enable privacy except for automotive apps which are "
+ "required by OEM.");
pw.println("");
}
diff --git a/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java b/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java
new file mode 100644
index 000000000000..7ee29016a58b
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import java.util.ArrayDeque;
+
+/**
+ * A generic grouped list of aggregated log records to be printed in dumpsys.
+ *
+ * <p>This can be used to dump history of operations or requests to the vibrator services, e.g.
+ * vibration requests grouped by usage or vibration parameters sent to the vibrator control service.
+ *
+ * @param <T> The type of log entries aggregated in this record.
+ */
+abstract class GroupedAggregatedLogRecords<T extends GroupedAggregatedLogRecords.SingleLogRecord> {
+ private final SparseArray<ArrayDeque<AggregatedLogRecord<T>>> mGroupedRecords;
+ private final int mSizeLimit;
+ private final int mAggregationTimeLimitMs;
+
+ GroupedAggregatedLogRecords(int sizeLimit, int aggregationTimeLimitMs) {
+ mGroupedRecords = new SparseArray<>();
+ mSizeLimit = sizeLimit;
+ mAggregationTimeLimitMs = aggregationTimeLimitMs;
+ }
+
+ /** Prints a header to identify the group to be logged. */
+ abstract void dumpGroupHeader(IndentingPrintWriter pw, int groupKey);
+
+ /** Returns the {@link ProtoOutputStream} repeated field id to log records of this group. */
+ abstract long findGroupKeyProtoFieldId(int groupKey);
+
+ /**
+ * Adds given entry to this record list, dropping the oldest record if size limit was reached
+ * for its group.
+ *
+ * @param record The new {@link SingleLogRecord} to be recorded.
+ * @return The oldest {@link AggregatedLogRecord} entry being dropped from the group list if
+ * it's full, null otherwise.
+ */
+ final synchronized AggregatedLogRecord<T> add(T record) {
+ int groupKey = record.getGroupKey();
+ if (!mGroupedRecords.contains(groupKey)) {
+ mGroupedRecords.put(groupKey, new ArrayDeque<>(mSizeLimit));
+ }
+ ArrayDeque<AggregatedLogRecord<T>> records = mGroupedRecords.get(groupKey);
+ if (mAggregationTimeLimitMs > 0 && !records.isEmpty()) {
+ AggregatedLogRecord<T> lastAggregatedRecord = records.getLast();
+ if (lastAggregatedRecord.mayAggregate(record, mAggregationTimeLimitMs)) {
+ lastAggregatedRecord.record(record);
+ return null;
+ }
+ }
+ AggregatedLogRecord<T> removedRecord = null;
+ if (records.size() >= mSizeLimit) {
+ removedRecord = records.removeFirst();
+ }
+ records.addLast(new AggregatedLogRecord<>(record));
+ return removedRecord;
+ }
+
+ final synchronized void dump(IndentingPrintWriter pw) {
+ for (int i = 0; i < mGroupedRecords.size(); i++) {
+ dumpGroupHeader(pw, mGroupedRecords.keyAt(i));
+ pw.increaseIndent();
+ for (AggregatedLogRecord<T> records : mGroupedRecords.valueAt(i)) {
+ records.dump(pw);
+ }
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+
+ final synchronized void dump(ProtoOutputStream proto) {
+ for (int i = 0; i < mGroupedRecords.size(); i++) {
+ long fieldId = findGroupKeyProtoFieldId(mGroupedRecords.keyAt(i));
+ for (AggregatedLogRecord<T> records : mGroupedRecords.valueAt(i)) {
+ records.dump(proto, fieldId);
+ }
+ }
+ }
+
+ /**
+ * Represents an aggregation of log record entries that can be printed in a compact manner.
+ *
+ * <p>The aggregation is controlled by a time limit on the difference between the creation time
+ * of two consecutive entries that {@link SingleLogRecord#mayAggregate}.
+ *
+ * @param <T> The type of log entries aggregated in this record.
+ */
+ static final class AggregatedLogRecord<T extends SingleLogRecord> {
+ private final T mFirst;
+ private T mLatest;
+ private int mCount;
+
+ AggregatedLogRecord(T record) {
+ mLatest = mFirst = record;
+ mCount = 1;
+ }
+
+ T getLatest() {
+ return mLatest;
+ }
+
+ synchronized boolean mayAggregate(T record, long timeLimitMs) {
+ long timeDeltaMs = Math.abs(mLatest.getCreateUptimeMs() - record.getCreateUptimeMs());
+ return mLatest.mayAggregate(record) && timeDeltaMs < timeLimitMs;
+ }
+
+ synchronized void record(T record) {
+ mLatest = record;
+ mCount++;
+ }
+
+ synchronized void dump(IndentingPrintWriter pw) {
+ mFirst.dump(pw);
+ if (mCount == 1) {
+ return;
+ }
+ if (mCount > 2) {
+ pw.println("-> Skipping " + (mCount - 2) + " aggregated entries, latest:");
+ }
+ mLatest.dump(pw);
+ }
+
+ synchronized void dump(ProtoOutputStream proto, long fieldId) {
+ mFirst.dump(proto, fieldId);
+ if (mCount > 1) {
+ mLatest.dump(proto, fieldId);
+ }
+ }
+ }
+
+ /**
+ * Represents a single log entry that can be grouped and aggregated for compact logging.
+ *
+ * <p>Entries are first grouped by an integer group key, and then aggregated with consecutive
+ * entries of same group within a limited timespan.
+ */
+ interface SingleLogRecord {
+
+ /** The group identifier for this record (e.g. vibration usage). */
+ int getGroupKey();
+
+ /**
+ * The timestamp in millis that should be used for aggregation of close entries.
+ *
+ * <p>Should be {@link SystemClock#uptimeMillis()} to be used for calculations.
+ */
+ long getCreateUptimeMs();
+
+ /**
+ * Returns true if this record can be aggregated with the given one (e.g. the represent the
+ * same vibration request from the same process client).
+ */
+ boolean mayAggregate(SingleLogRecord record);
+
+ /** Writes this record into given {@link IndentingPrintWriter}. */
+ void dump(IndentingPrintWriter pw);
+
+ /** Writes this record into given {@link ProtoOutputStream} field. */
+ void dump(ProtoOutputStream proto, long fieldId);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 70e2e27a3bae..8f755f4ecec8 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -54,12 +54,18 @@ final class HalVibration extends Vibration {
/** Vibration status. */
private Vibration.Status mStatus;
+ /** Reported scale values applied to the vibration effects. */
+ private int mScaleLevel;
+ private float mAdaptiveScale;
+
HalVibration(@NonNull IBinder token, @NonNull CombinedVibration effect,
@NonNull CallerInfo callerInfo) {
super(token, callerInfo);
mOriginalEffect = effect;
mEffectToPlay = effect;
mStatus = Vibration.Status.RUNNING;
+ mScaleLevel = VibrationScaler.SCALE_NONE;
+ mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE;
}
/**
@@ -119,20 +125,24 @@ final class HalVibration extends Vibration {
}
/**
- * Scales the {@link #getEffectToPlay()} and each fallback effect with a scaling transformation.
- *
- * @param scaler A {@link VibrationEffect.Transformation<Integer>} that takes one of the
- * {@code VibrationAttributes.USAGE_*} as the modifier to scale the effect
- * based on the user settings.
+ * Scales the {@link #getEffectToPlay()} and each fallback effect based on the vibration usage.
*/
- public void scaleEffects(VibrationEffect.Transformation<Integer> scaler) {
+ public void scaleEffects(VibrationScaler scaler) {
int vibrationUsage = callerInfo.attrs.getUsage();
- CombinedVibration newEffect = mEffectToPlay.transform(scaler, vibrationUsage);
+
+ // Save scale values for debugging purposes.
+ mScaleLevel = scaler.getScaleLevel(vibrationUsage);
+ mAdaptiveScale = scaler.getAdaptiveHapticsScale(vibrationUsage);
+
+ // Scale all VibrationEffect instances in given CombinedVibration.
+ CombinedVibration newEffect = mEffectToPlay.transform(scaler::scale, vibrationUsage);
if (!Objects.equals(mEffectToPlay, newEffect)) {
mEffectToPlay = newEffect;
}
+
+ // Scale all fallback VibrationEffect instances that can be used by VibrationThread.
for (int i = 0; i < mFallbacks.size(); i++) {
- mFallbacks.setValueAt(i, scaler.transform(mFallbacks.valueAt(i), vibrationUsage));
+ mFallbacks.setValueAt(i, scaler.scale(mFallbacks.valueAt(i), vibrationUsage));
}
}
@@ -171,7 +181,7 @@ final class HalVibration extends Vibration {
CombinedVibration originalEffect =
Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect;
return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect,
- /* scale= */ 0, callerInfo);
+ mScaleLevel, mAdaptiveScale, callerInfo);
}
/** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
index a34621642bcd..9756094e5af0 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -19,8 +19,8 @@ package com.android.server.vibrator;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.VibrationEffect;
-import android.os.vibrator.Flags;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
import android.os.vibrator.persistence.ParsedVibration;
import android.os.vibrator.persistence.VibrationXmlParser;
import android.text.TextUtils;
@@ -73,8 +73,6 @@ import java.io.IOException;
*
* <p>After a successful parsing of the customization XML file, it returns a {@link SparseArray}
* that maps each customized haptic feedback effect ID to its respective {@link VibrationEffect}.
- *
- * @hide
*/
final class HapticFeedbackCustomization {
private static final String TAG = "HapticFeedbackCustomization";
@@ -104,8 +102,6 @@ final class HapticFeedbackCustomization {
* @throws {@link IOException} if an IO error occurs while parsing the customization XML.
* @throws {@link CustomizationParserException} for any non-IO error that occurs when parsing
* the XML, like an invalid XML content or an invalid haptic feedback constant.
- *
- * @hide
*/
@Nullable
static SparseArray<VibrationEffect> loadVibrations(Resources res, VibratorInfo vibratorInfo)
@@ -202,8 +198,6 @@ final class HapticFeedbackCustomization {
/**
* Represents an error while parsing a haptic feedback customization XML.
- *
- * @hide
*/
static final class CustomizationParserException extends Exception {
private CustomizationParserException(String message) {
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 519acec2f7b4..96f045d7e258 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -34,8 +34,6 @@ import java.io.PrintWriter;
/**
* Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
- *
- * @hide
*/
public final class HapticFeedbackVibrationProvider {
private static final String TAG = "HapticFeedbackVibrationProvider";
@@ -58,17 +56,14 @@ public final class HapticFeedbackVibrationProvider {
private float mKeyboardVibrationFixedAmplitude;
- /** @hide */
public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
this(res, vibrator.getInfo());
}
- /** @hide */
public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
}
- /** @hide */
@VisibleForTesting HapticFeedbackVibrationProvider(
Resources res,
VibratorInfo vibratorInfo,
@@ -190,10 +185,11 @@ public final class HapticFeedbackVibrationProvider {
* to get.
* @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass
* vibration intensity settings. {@code false} otherwise.
+ * @param fromIme the haptic feedback is performed from an IME.
* @return the {@link VibrationAttributes} that should be used for the provided haptic feedback.
*/
public VibrationAttributes getVibrationAttributesForHapticFeedback(
- int effectId, boolean bypassVibrationIntensitySetting) {
+ int effectId, boolean bypassVibrationIntensitySetting, boolean fromIme) {
VibrationAttributes attrs;
switch (effectId) {
case HapticFeedbackConstants.EDGE_SQUEEZE:
@@ -209,7 +205,7 @@ public final class HapticFeedbackVibrationProvider {
break;
case HapticFeedbackConstants.KEYBOARD_TAP:
case HapticFeedbackConstants.KEYBOARD_RELEASE:
- attrs = createKeyboardVibrationAttributes();
+ attrs = createKeyboardVibrationAttributes(fromIme);
break;
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
@@ -222,7 +218,7 @@ public final class HapticFeedbackVibrationProvider {
if (shouldBypassInterruptionPolicy(effectId)) {
flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
}
- if (shouldBypassIntensityScale(effectId)) {
+ if (shouldBypassIntensityScale(effectId, fromIme)) {
flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
}
@@ -337,9 +333,9 @@ public final class HapticFeedbackVibrationProvider {
/* fallbackForPredefinedEffect= */ predefinedEffectFallback);
}
- private boolean shouldBypassIntensityScale(int effectId) {
- if (!Flags.keyboardCategoryEnabled() || mKeyboardVibrationFixedAmplitude < 0) {
- // shouldn't bypass if not support keyboard category or no fixed amplitude
+ private boolean shouldBypassIntensityScale(int effectId, boolean isIme) {
+ if (!Flags.keyboardCategoryEnabled() || mKeyboardVibrationFixedAmplitude < 0 || !isIme) {
+ // Shouldn't bypass if not support keyboard category, no fixed amplitude or not an IME.
return false;
}
switch (effectId) {
@@ -353,8 +349,9 @@ public final class HapticFeedbackVibrationProvider {
return false;
}
- private static VibrationAttributes createKeyboardVibrationAttributes() {
- if (!Flags.keyboardCategoryEnabled()) {
+ private VibrationAttributes createKeyboardVibrationAttributes(boolean fromIme) {
+ // Use touch attribute when the keyboard category is disable or it's not from an IME.
+ if (!Flags.keyboardCategoryEnabled() || !fromIme) {
return TOUCH_VIBRATION_ATTRIBUTES;
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index b2e808ac8e95..b490f57a936e 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -218,12 +218,13 @@ abstract class Vibration {
private final long mDurationMs;
@Nullable
private final CombinedVibration mOriginalEffect;
- private final float mScale;
+ private final int mScaleLevel;
+ private final float mAdaptiveScale;
private final Status mStatus;
DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect,
- @Nullable CombinedVibration originalEffect, float scale,
- @NonNull CallerInfo callerInfo) {
+ @Nullable CombinedVibration originalEffect, int scaleLevel,
+ float adaptiveScale, @NonNull CallerInfo callerInfo) {
Objects.requireNonNull(callerInfo);
mCreateTime = stats.getCreateTimeDebug();
mStartTime = stats.getStartTimeDebug();
@@ -231,7 +232,8 @@ abstract class Vibration {
mDurationMs = stats.getDurationDebug();
mPlayedEffect = playedEffect;
mOriginalEffect = originalEffect;
- mScale = scale;
+ mScaleLevel = scaleLevel;
+ mAdaptiveScale = adaptiveScale;
mCallerInfo = callerInfo;
mStatus = status;
}
@@ -246,7 +248,8 @@ abstract class Vibration {
+ ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
+ ", playedEffect: " + mPlayedEffect
+ ", originalEffect: " + mOriginalEffect
- + ", scale: " + String.format(Locale.ROOT, "%.2f", mScale)
+ + ", scaleLevel: " + VibrationScaler.scaleLevelToString(mScaleLevel)
+ + ", adaptiveScale: " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale)
+ ", callerInfo: " + mCallerInfo;
}
@@ -259,26 +262,39 @@ abstract class Vibration {
void dumpCompact(IndentingPrintWriter pw) {
boolean isExternalVibration = mPlayedEffect == null;
String timingsStr = String.format(Locale.ROOT,
- "%s | %8s | %20s | duration: %5dms | start: %12s | end: %10s",
+ "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s",
DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)),
isExternalVibration ? "external" : "effect",
mStatus.name().toLowerCase(Locale.ROOT),
mDurationMs,
mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
- String callerInfoStr = String.format(Locale.ROOT,
- " | %s (uid=%d, deviceId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
- mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId,
- mCallerInfo.attrs.usageToString(),
- AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
+ String paramStr = String.format(Locale.ROOT,
+ " | scale: %8s (adaptive=%.2f) | flags: %4s | usage: %s",
+ VibrationScaler.scaleLevelToString(mScaleLevel), mAdaptiveScale,
Long.toBinaryString(mCallerInfo.attrs.getFlags()),
- mCallerInfo.reason);
+ mCallerInfo.attrs.usageToString());
+ // Optional, most vibrations have category unknown so skip them to simplify the logs
+ String categoryStr =
+ mCallerInfo.attrs.getCategory() != VibrationAttributes.CATEGORY_UNKNOWN
+ ? " | category=" + VibrationAttributes.categoryToString(
+ mCallerInfo.attrs.getCategory())
+ : "";
+ // Optional, most vibrations should not be defined via AudioAttributes
+ // so skip them to simplify the logs
+ String audioUsageStr =
+ mCallerInfo.attrs.getOriginalAudioUsage() != AudioAttributes.USAGE_UNKNOWN
+ ? " | audioUsage=" + AudioAttributes.usageToString(
+ mCallerInfo.attrs.getOriginalAudioUsage())
+ : "";
+ String callerStr = String.format(Locale.ROOT,
+ " | %s (uid=%d, deviceId=%d) | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId, mCallerInfo.reason);
String effectStr = String.format(Locale.ROOT,
- " | played: %s | original: %s | scale: %.2f",
+ " | played: %s | original: %s",
mPlayedEffect == null ? null : mPlayedEffect.toDebugString(),
- mOriginalEffect == null ? null : mOriginalEffect.toDebugString(),
- mScale);
- pw.println(timingsStr + callerInfoStr + effectStr);
+ mOriginalEffect == null ? null : mOriginalEffect.toDebugString());
+ pw.println(timingsStr + paramStr + categoryStr + audioUsageStr + callerStr + effectStr);
}
/** Write this info into given {@link PrintWriter}. */
@@ -293,7 +309,8 @@ abstract class Vibration {
+ (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime))));
pw.println("playedEffect = " + mPlayedEffect);
pw.println("originalEffect = " + mOriginalEffect);
- pw.println("scale = " + String.format(Locale.ROOT, "%.2f", mScale));
+ pw.println("scale = " + VibrationScaler.scaleLevelToString(mScaleLevel));
+ pw.println("adaptiveScale = " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale));
pw.println("callerInfo = " + mCallerInfo);
pw.decreaseIndent();
}
@@ -310,6 +327,7 @@ abstract class Vibration {
final VibrationAttributes attrs = mCallerInfo.attrs;
proto.write(VibrationAttributesProto.USAGE, attrs.getUsage());
proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage());
+ proto.write(VibrationAttributesProto.CATEGORY, attrs.getCategory());
proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags());
proto.end(attrsToken);
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 5d17884c769b..d9ca71003aae 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -26,10 +26,14 @@ import android.os.Vibrator;
import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Locale;
/** Controls vibration scaling. */
final class VibrationScaler {
@@ -37,13 +41,12 @@ final class VibrationScaler {
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
// and the default intensity for that type of vibration (i.e. current - default).
- private static final int SCALE_VERY_LOW =
- ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
- private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
- private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
- private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
- private static final int SCALE_VERY_HIGH =
- ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2
+ static final int SCALE_VERY_LOW = ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
+ static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
+ static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
+ static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
+ static final int SCALE_VERY_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2
+ static final float ADAPTIVE_SCALE_NONE = 1f;
// Scale factors for each level.
private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
@@ -52,6 +55,8 @@ final class VibrationScaler {
private static final float SCALE_FACTOR_HIGH = 1.2f;
private static final float SCALE_FACTOR_VERY_HIGH = 1.4f;
+ private static final ScaleLevel SCALE_LEVEL_NONE = new ScaleLevel(SCALE_FACTOR_NONE);
+
// A mapping from the intensity adjustment to the scaling to apply, where the intensity
// adjustment is defined as the delta between the default intensity level and the user selected
// intensity level. It's important that we apply the scaling on the delta between the two so
@@ -69,7 +74,7 @@ final class VibrationScaler {
mScaleLevels = new SparseArray<>();
mScaleLevels.put(SCALE_VERY_LOW, new ScaleLevel(SCALE_FACTOR_VERY_LOW));
mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_FACTOR_LOW));
- mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_FACTOR_NONE));
+ mScaleLevels.put(SCALE_NONE, SCALE_LEVEL_NONE);
mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH));
mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH));
}
@@ -87,25 +92,24 @@ final class VibrationScaler {
* @param usageHint one of VibrationAttributes.USAGE_*
* @return one of ExternalVibrationScale.ScaleLevel.SCALE_*
*/
- public int getExternalVibrationScaleLevel(int usageHint) {
+ public int getScaleLevel(int usageHint) {
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
-
if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
// Bypassing user settings, or it has changed between checking and scaling. Use default.
return SCALE_NONE;
}
int scaleLevel = currentIntensity - defaultIntensity;
-
if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
return scaleLevel;
- } else {
- // Something about our scaling has gone wrong, so just play with no scaling.
- Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level "
- + scaleLevel + " for vibration with usage " + usageHint);
- return SCALE_NONE;
}
+
+ // Something about our scaling has gone wrong, so just play with no scaling.
+ Slog.wtf(TAG, "Error in scaling calculations, ended up with invalid scale level "
+ + scaleLevel + " for vibration with usage " + usageHint);
+
+ return SCALE_NONE;
}
/**
@@ -117,11 +121,9 @@ final class VibrationScaler {
* @return The adaptive haptics scale.
*/
public float getAdaptiveHapticsScale(int usageHint) {
- if (shouldApplyAdaptiveHapticsScale(usageHint)) {
- return mAdaptiveHapticsScales.get(usageHint);
- }
-
- return 1f; // no scaling
+ return Flags.adaptiveHapticsEnabled()
+ ? mAdaptiveHapticsScales.get(usageHint, ADAPTIVE_SCALE_NONE)
+ : ADAPTIVE_SCALE_NONE;
}
/**
@@ -140,21 +142,16 @@ final class VibrationScaler {
return effect;
}
- int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
- int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
-
- if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- // Bypassing user settings, or it has changed between checking and scaling. Use default.
- currentIntensity = defaultIntensity;
- }
-
- int newEffectStrength = intensityToEffectStrength(currentIntensity);
- ScaleLevel scaleLevel = mScaleLevels.get(currentIntensity - defaultIntensity);
+ int newEffectStrength = getEffectStrength(usageHint);
+ ScaleLevel scaleLevel = mScaleLevels.get(getScaleLevel(usageHint));
+ float adaptiveScale = getAdaptiveHapticsScale(usageHint);
if (scaleLevel == null) {
// Something about our scaling has gone wrong, so just play with no scaling.
- Slog.e(TAG, "No configured scaling level!"
- + " (current=" + currentIntensity + ", default= " + defaultIntensity + ")");
+ Slog.e(TAG, "No configured scaling level found! (current="
+ + mSettingsController.getCurrentIntensity(usageHint) + ", default= "
+ + mSettingsController.getDefaultIntensity(usageHint) + ")");
+ scaleLevel = SCALE_LEVEL_NONE;
}
VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect;
@@ -162,20 +159,11 @@ final class VibrationScaler {
new ArrayList<>(composedEffect.getSegments());
int segmentCount = segments.size();
for (int i = 0; i < segmentCount; i++) {
- VibrationEffectSegment segment = segments.get(i);
- segment = segment.resolve(mDefaultVibrationAmplitude)
- .applyEffectStrength(newEffectStrength);
- if (scaleLevel != null) {
- segment = segment.scale(scaleLevel.factor);
- }
-
- // If adaptive haptics scaling is available for this usage, apply it to the segment.
- if (shouldApplyAdaptiveHapticsScale(usageHint)) {
- float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
- segment = segment.scaleLinearly(adaptiveScale);
- }
-
- segments.set(i, segment);
+ segments.set(i,
+ segments.get(i).resolve(mDefaultVibrationAmplitude)
+ .applyEffectStrength(newEffectStrength)
+ .scale(scaleLevel.factor)
+ .scaleLinearly(adaptiveScale));
}
if (segments.equals(composedEffect.getSegments())) {
// No segment was updated, return original effect.
@@ -197,15 +185,7 @@ final class VibrationScaler {
* updated effect strength
*/
public PrebakedSegment scale(PrebakedSegment prebaked, int usageHint) {
- int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
-
- if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- // Bypassing user settings, or it has changed between checking and scaling. Use default.
- currentIntensity = mSettingsController.getDefaultIntensity(usageHint);
- }
-
- int newEffectStrength = intensityToEffectStrength(currentIntensity);
- return prebaked.applyEffectStrength(newEffectStrength);
+ return prebaked.applyEffectStrength(getEffectStrength(usageHint));
}
/**
@@ -213,8 +193,6 @@ final class VibrationScaler {
*
* @param usageHint one of VibrationAttributes.USAGE_*.
* @param scale The scaling factor that should be applied to vibrations of this usage.
- *
- * @hide
*/
public void updateAdaptiveHapticsScale(@VibrationAttributes.Usage int usageHint, float scale) {
mAdaptiveHapticsScales.put(usageHint, scale);
@@ -224,24 +202,68 @@ final class VibrationScaler {
* Removes the usage from the cached adaptive haptics scales list.
*
* @param usageHint one of VibrationAttributes.USAGE_*.
- *
- * @hide
*/
public void removeAdaptiveHapticsScale(@VibrationAttributes.Usage int usageHint) {
mAdaptiveHapticsScales.remove(usageHint);
}
- /**
- * Removes all cached adaptive haptics scales.
- *
- * @hide
- */
+ /** Removes all cached adaptive haptics scales. */
public void clearAdaptiveHapticsScales() {
mAdaptiveHapticsScales.clear();
}
- private boolean shouldApplyAdaptiveHapticsScale(int usageHint) {
- return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint);
+ /** Write current settings into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ pw.println("VibrationScaler:");
+ pw.increaseIndent();
+ pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude);
+
+ pw.println("ScaleLevels:");
+ pw.increaseIndent();
+ for (int i = 0; i < mScaleLevels.size(); i++) {
+ int scaleLevelKey = mScaleLevels.keyAt(i);
+ ScaleLevel scaleLevel = mScaleLevels.valueAt(i);
+ pw.println(scaleLevelToString(scaleLevelKey) + " = " + scaleLevel);
+ }
+ pw.decreaseIndent();
+
+ pw.println("AdaptiveHapticsScales:");
+ pw.increaseIndent();
+ for (int i = 0; i < mAdaptiveHapticsScales.size(); i++) {
+ int usage = mAdaptiveHapticsScales.keyAt(i);
+ float scale = mAdaptiveHapticsScales.valueAt(i);
+ pw.println(VibrationAttributes.usageToString(usage)
+ + " = " + String.format(Locale.ROOT, "%.2f", scale));
+ }
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+
+ /** Write current settings into given {@link ProtoOutputStream}. */
+ void dump(ProtoOutputStream proto) {
+ proto.write(VibratorManagerServiceDumpProto.DEFAULT_VIBRATION_AMPLITUDE,
+ mDefaultVibrationAmplitude);
+ }
+
+ @Override
+ public String toString() {
+ return "VibrationScaler{"
+ + "mScaleLevels=" + mScaleLevels
+ + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude
+ + ", mAdaptiveHapticsScales=" + mAdaptiveHapticsScales
+ + '}';
+ }
+
+ private int getEffectStrength(int usageHint) {
+ int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+ if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ // Bypassing user settings, or it has changed between checking and scaling. Use default.
+ currentIntensity = mSettingsController.getDefaultIntensity(usageHint);
+ }
+
+ return intensityToEffectStrength(currentIntensity);
}
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
@@ -259,6 +281,17 @@ final class VibrationScaler {
}
}
+ static String scaleLevelToString(int scaleLevel) {
+ return switch (scaleLevel) {
+ case SCALE_VERY_LOW -> "VERY_LOW";
+ case SCALE_LOW -> "LOW";
+ case SCALE_NONE -> "NONE";
+ case SCALE_HIGH -> "HIGH";
+ case SCALE_VERY_HIGH -> "VERY_HIGH";
+ default -> String.valueOf(scaleLevel);
+ };
+ }
+
/** Represents the scale that must be applied to a vibration effect intensity. */
private static final class ScaleLevel {
public final float factor;
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 99ce3e2fb740..5b77433fa6d9 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -386,7 +386,6 @@ final class VibrationSettings {
* Returns the duration, in milliseconds, that the vibrator control service will wait for new
* vibration params.
* @return The request vibration params timeout in milliseconds.
- * @hide
*/
public int getRequestVibrationParamsTimeoutMs() {
return mVibrationConfig.getRequestVibrationParamsTimeoutMs();
@@ -645,11 +644,16 @@ final class VibrationSettings {
.append("), ");
}
vibrationIntensitiesString.append('}');
+ String keyboardVibrationOnString = mKeyboardVibrationOn
+ + " (default: " + mVibrationConfig.isDefaultKeyboardVibrationEnabled() + ")";
return "VibrationSettings{"
+ "mVibratorConfig=" + mVibrationConfig
+ + ", mVibrateOn=" + mVibrateOn
+ + ", mKeyboardVibrationOn=" + keyboardVibrationOnString
+ ", mVibrateInputDevices=" + mVibrateInputDevices
+ ", mBatterySaverMode=" + mBatterySaverMode
- + ", mVibrateOn=" + mVibrateOn
+ + ", mRingerMode=" + ringerModeToString(mRingerMode)
+ + ", mOnWirelessCharger=" + mOnWirelessCharger
+ ", mVibrationIntensities=" + vibrationIntensitiesString
+ ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ '}';
@@ -658,32 +662,40 @@ final class VibrationSettings {
/** Write current settings into given {@link PrintWriter}. */
void dump(IndentingPrintWriter pw) {
- pw.println("VibrationSettings:");
- pw.increaseIndent();
- pw.println("vibrateOn = " + mVibrateOn);
- pw.println("vibrateInputDevices = " + mVibrateInputDevices);
- pw.println("batterySaverMode = " + mBatterySaverMode);
- pw.println("VibrationIntensities:");
+ synchronized (mLock) {
+ pw.println("VibrationSettings:");
+ pw.increaseIndent();
+ pw.println("vibrateOn = " + mVibrateOn);
+ pw.println("keyboardVibrationOn = " + mKeyboardVibrationOn
+ + ", default: " + mVibrationConfig.isDefaultKeyboardVibrationEnabled());
+ pw.println("vibrateInputDevices = " + mVibrateInputDevices);
+ pw.println("batterySaverMode = " + mBatterySaverMode);
+ pw.println("ringerMode = " + ringerModeToString(mRingerMode));
+ pw.println("onWirelessCharger = " + mOnWirelessCharger);
+ pw.println("processStateCache size = " + mUidObserver.mProcStatesCache.size());
+
+ pw.println("VibrationIntensities:");
+ pw.increaseIndent();
+ for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
+ int usage = mCurrentVibrationIntensities.keyAt(i);
+ int intensity = mCurrentVibrationIntensities.valueAt(i);
+ pw.println(VibrationAttributes.usageToString(usage) + " = "
+ + intensityToString(intensity)
+ + ", default: " + intensityToString(getDefaultIntensity(usage)));
+ }
+ pw.decreaseIndent();
- pw.increaseIndent();
- for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
- int usage = mCurrentVibrationIntensities.keyAt(i);
- int intensity = mCurrentVibrationIntensities.valueAt(i);
- pw.println(VibrationAttributes.usageToString(usage) + " = "
- + intensityToString(intensity)
- + ", default: " + intensityToString(getDefaultIntensity(usage)));
+ mVibrationConfig.dumpWithoutDefaultSettings(pw);
+ pw.decreaseIndent();
}
- pw.decreaseIndent();
-
- mVibrationConfig.dumpWithoutDefaultSettings(pw);
- pw.println("processStateCache = " + mUidObserver.mProcStatesCache);
- pw.decreaseIndent();
}
/** Write current settings into given {@link ProtoOutputStream}. */
void dump(ProtoOutputStream proto) {
synchronized (mLock) {
proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
+ proto.write(VibratorManagerServiceDumpProto.KEYBOARD_VIBRATION_ON,
+ mKeyboardVibrationOn);
proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
getCurrentIntensity(USAGE_ALARM));
@@ -723,18 +735,22 @@ final class VibrationSettings {
}
private static String intensityToString(int intensity) {
- switch (intensity) {
- case Vibrator.VIBRATION_INTENSITY_OFF:
- return "OFF";
- case Vibrator.VIBRATION_INTENSITY_LOW:
- return "LOW";
- case Vibrator.VIBRATION_INTENSITY_MEDIUM:
- return "MEDIUM";
- case Vibrator.VIBRATION_INTENSITY_HIGH:
- return "HIGH";
- default:
- return "UNKNOWN INTENSITY " + intensity;
- }
+ return switch (intensity) {
+ case Vibrator.VIBRATION_INTENSITY_OFF -> "OFF";
+ case Vibrator.VIBRATION_INTENSITY_LOW -> "LOW";
+ case Vibrator.VIBRATION_INTENSITY_MEDIUM -> "MEDIUM";
+ case Vibrator.VIBRATION_INTENSITY_HIGH -> "HIGH";
+ default -> "UNKNOWN INTENSITY " + intensity;
+ };
+ }
+
+ private static String ringerModeToString(int ringerMode) {
+ return switch (ringerMode) {
+ case AudioManager.RINGER_MODE_SILENT -> "silent";
+ case AudioManager.RINGER_MODE_VIBRATE -> "vibrate";
+ case AudioManager.RINGER_MODE_NORMAL -> "normal";
+ default -> String.valueOf(ringerMode);
+ };
}
@VibrationIntensity
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index f6af9ad991ff..f510b4e8ab30 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -161,7 +161,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
waitForVibrationParamsIfRequired();
}
// Scale resolves the default amplitudes from the effect before scaling them.
- mVibration.scaleEffects(mVibrationScaler::scale);
+ mVibration.scaleEffects(mVibrationScaler);
} else {
mVibration.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index 17a9e3330375..ec3d99b24656 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -30,6 +30,7 @@ import static android.os.VibrationAttributes.USAGE_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.content.Context;
import android.frameworks.vibrator.IVibratorControlService;
import android.frameworks.vibrator.IVibratorController;
import android.frameworks.vibrator.ScaleParam;
@@ -37,27 +38,38 @@ import android.frameworks.vibrator.VibrationParam;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
/**
* Implementation of {@link IVibratorControlService} which allows the registration of
* {@link IVibratorController} to set and receive vibration params.
- *
- * @hide
*/
-public final class VibratorControlService extends IVibratorControlService.Stub {
+final class VibratorControlService extends IVibratorControlService.Stub {
private static final String TAG = "VibratorControlService";
private static final int UNRECOGNIZED_VIBRATION_TYPE = -1;
private static final int NO_SCALE = -1;
+ private static final SimpleDateFormat DEBUG_DATE_TIME_FORMAT =
+ new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
+ private final VibrationParamsRecords mVibrationParamsRecords;
private final VibratorControllerHolder mVibratorControllerHolder;
private final VibrationScaler mVibrationScaler;
private final Object mLock;
@@ -68,25 +80,32 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
@GuardedBy("mLock")
private IBinder mRequestVibrationParamsToken;
- public VibratorControlService(VibratorControllerHolder vibratorControllerHolder,
- VibrationScaler vibrationScaler, VibrationSettings vibrationSettings, Object lock) {
+ VibratorControlService(Context context,
+ VibratorControllerHolder vibratorControllerHolder, VibrationScaler vibrationScaler,
+ VibrationSettings vibrationSettings, Object lock) {
mVibratorControllerHolder = vibratorControllerHolder;
mVibrationScaler = vibrationScaler;
mLock = lock;
mRequestVibrationParamsForUsages = vibrationSettings.getRequestVibrationParamsForUsages();
+
+ int dumpSizeLimit = context.getResources().getInteger(
+ com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit);
+ int dumpAggregationTimeLimit = context.getResources().getInteger(
+ com.android.internal.R.integer
+ .config_previousVibrationsDumpAggregationTimeMillisLimit);
+ mVibrationParamsRecords =
+ new VibrationParamsRecords(dumpSizeLimit, dumpAggregationTimeLimit);
}
@Override
- public void registerVibratorController(IVibratorController controller)
- throws RemoteException {
+ public void registerVibratorController(IVibratorController controller) {
synchronized (mLock) {
mVibratorControllerHolder.setVibratorController(controller);
}
}
@Override
- public void unregisterVibratorController(@NonNull IVibratorController controller)
- throws RemoteException {
+ public void unregisterVibratorController(@NonNull IVibratorController controller) {
Objects.requireNonNull(controller);
synchronized (mLock) {
@@ -110,7 +129,7 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
@Override
public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params,
- @NonNull IVibratorController token) throws RemoteException {
+ @NonNull IVibratorController token) {
Objects.requireNonNull(token);
synchronized (mLock) {
@@ -128,12 +147,12 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
updateAdaptiveHapticsScales(params);
+ recordUpdateVibrationParams(params, /* fromRequest= */ false);
}
}
@Override
- public void clearVibrationParams(int types, @NonNull IVibratorController token)
- throws RemoteException {
+ public void clearVibrationParams(int types, @NonNull IVibratorController token) {
Objects.requireNonNull(token);
synchronized (mLock) {
@@ -151,13 +170,13 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
updateAdaptiveHapticsScales(types, NO_SCALE);
+ recordClearVibrationParams(types);
}
}
@Override
public void onRequestVibrationParamsComplete(
- @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
- throws RemoteException {
+ @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) {
Objects.requireNonNull(requestToken);
synchronized (mLock) {
@@ -177,16 +196,17 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
updateAdaptiveHapticsScales(result);
endOngoingRequestVibrationParamsLocked(/* wasCancelled= */ false);
+ recordUpdateVibrationParams(result, /* fromRequest= */ true);
}
}
@Override
- public int getInterfaceVersion() throws RemoteException {
+ public int getInterfaceVersion() {
return this.VERSION;
}
@Override
- public String getInterfaceHash() throws RemoteException {
+ public String getInterfaceHash() {
return this.HASH;
}
@@ -266,6 +286,42 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
}
+ /** Write current settings into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ boolean isVibratorControllerRegistered;
+ boolean hasPendingVibrationParamsRequest;
+ synchronized (mLock) {
+ isVibratorControllerRegistered =
+ mVibratorControllerHolder.getVibratorController() != null;
+ hasPendingVibrationParamsRequest = mRequestVibrationParamsFuture != null;
+ }
+
+ pw.println("VibratorControlService:");
+ pw.increaseIndent();
+ pw.println("isVibratorControllerRegistered = " + isVibratorControllerRegistered);
+ pw.println("hasPendingVibrationParamsRequest = " + hasPendingVibrationParamsRequest);
+
+ pw.println();
+ pw.println("Vibration parameters update history:");
+ pw.increaseIndent();
+ mVibrationParamsRecords.dump(pw);
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+
+ /** Write current settings into given {@link ProtoOutputStream}. */
+ void dump(ProtoOutputStream proto) {
+ boolean isVibratorControllerRegistered;
+ synchronized (mLock) {
+ isVibratorControllerRegistered =
+ mVibratorControllerHolder.getVibratorController() != null;
+ }
+ proto.write(VibratorManagerServiceDumpProto.IS_VIBRATOR_CONTROLLER_REGISTERED,
+ isVibratorControllerRegistered);
+ mVibrationParamsRecords.dump(proto);
+ }
+
/**
* Completes or cancels the vibration params request future and resets the future and token
* to null.
@@ -312,6 +368,33 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
}
+ private static int[] mapFromAdaptiveVibrationTypeToVibrationUsages(int types) {
+ IntArray usages = new IntArray(15);
+ if ((ScaleParam.TYPE_ALARM & types) != 0) {
+ usages.add(USAGE_ALARM);
+ }
+
+ if ((ScaleParam.TYPE_NOTIFICATION & types) != 0) {
+ usages.add(USAGE_NOTIFICATION);
+ usages.add(USAGE_COMMUNICATION_REQUEST);
+ }
+
+ if ((ScaleParam.TYPE_RINGTONE & types) != 0) {
+ usages.add(USAGE_RINGTONE);
+ }
+
+ if ((ScaleParam.TYPE_MEDIA & types) != 0) {
+ usages.add(USAGE_MEDIA);
+ usages.add(USAGE_UNKNOWN);
+ }
+
+ if ((ScaleParam.TYPE_INTERACTIVE & types) != 0) {
+ usages.add(USAGE_TOUCH);
+ usages.add(USAGE_HARDWARE_FEEDBACK);
+ }
+ return usages.toArray();
+ }
+
/**
* Updates the adaptive haptics scales cached in {@link VibrationScaler} with the
* provided params.
@@ -319,7 +402,14 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
* @param params the new vibration params.
*/
private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) {
+ if (params == null) {
+ return;
+ }
for (VibrationParam param : params) {
+ if (param.getTag() != VibrationParam.scale) {
+ Slog.e(TAG, "Unsupported vibration param: " + param);
+ continue;
+ }
ScaleParam scaleParam = param.getScale();
updateAdaptiveHapticsScales(scaleParam.typesMask, scaleParam.scale);
}
@@ -333,27 +423,8 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
* @param scale The scaling factor that should be applied to the vibrations.
*/
private void updateAdaptiveHapticsScales(int types, float scale) {
- if ((ScaleParam.TYPE_ALARM & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_ALARM, scale);
- }
-
- if ((ScaleParam.TYPE_NOTIFICATION & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_NOTIFICATION, scale);
- updateOrRemoveAdaptiveHapticsScale(USAGE_COMMUNICATION_REQUEST, scale);
- }
-
- if ((ScaleParam.TYPE_RINGTONE & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_RINGTONE, scale);
- }
-
- if ((ScaleParam.TYPE_MEDIA & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_MEDIA, scale);
- updateOrRemoveAdaptiveHapticsScale(USAGE_UNKNOWN, scale);
- }
-
- if ((ScaleParam.TYPE_INTERACTIVE & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_TOUCH, scale);
- updateOrRemoveAdaptiveHapticsScale(USAGE_HARDWARE_FEEDBACK, scale);
+ for (int usage : mapFromAdaptiveVibrationTypeToVibrationUsages(types)) {
+ updateOrRemoveAdaptiveHapticsScale(usage, scale);
}
}
@@ -375,4 +446,136 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
mVibrationScaler.updateAdaptiveHapticsScale(usageHint, scale);
}
+
+ private void recordUpdateVibrationParams(@Nullable VibrationParam[] params,
+ boolean fromRequest) {
+ if (params == null) {
+ return;
+ }
+ VibrationParamsRecords.Operation operation =
+ fromRequest ? VibrationParamsRecords.Operation.PULL
+ : VibrationParamsRecords.Operation.PUSH;
+ long createTime = SystemClock.uptimeMillis();
+ for (VibrationParam param : params) {
+ if (param.getTag() != VibrationParam.scale) {
+ Slog.w(TAG, "Unsupported vibration param ignored from dumpsys records: " + param);
+ continue;
+ }
+ ScaleParam scaleParam = param.getScale();
+ mVibrationParamsRecords.add(new VibrationScaleParamRecord(operation, createTime,
+ scaleParam.typesMask, scaleParam.scale));
+ }
+ }
+
+ private void recordClearVibrationParams(int typesMask) {
+ long createTime = SystemClock.uptimeMillis();
+ mVibrationParamsRecords.add(new VibrationScaleParamRecord(
+ VibrationParamsRecords.Operation.CLEAR, createTime, typesMask, NO_SCALE));
+ }
+
+ /**
+ * Keep records of {@link VibrationParam} values received by this service from a registered
+ * {@link VibratorController} and provide debug information for this service.
+ */
+ private static final class VibrationParamsRecords
+ extends GroupedAggregatedLogRecords<VibrationScaleParamRecord> {
+
+ /** The type of operations on vibration parameters that the service is recording. */
+ enum Operation {
+ PULL, PUSH, CLEAR
+ };
+
+ VibrationParamsRecords(int sizeLimit, int aggregationTimeLimit) {
+ super(sizeLimit, aggregationTimeLimit);
+ }
+
+ @Override
+ synchronized void dumpGroupHeader(IndentingPrintWriter pw, int paramType) {
+ if (paramType == VibrationParam.scale) {
+ pw.println("SCALE:");
+ } else {
+ pw.println("UNKNOWN:");
+ }
+ }
+
+ @Override
+ synchronized long findGroupKeyProtoFieldId(int usage) {
+ return VibratorManagerServiceDumpProto.PREVIOUS_VIBRATION_PARAMS;
+ }
+ }
+
+ /**
+ * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
+ * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
+ */
+ private static final class VibrationScaleParamRecord
+ implements GroupedAggregatedLogRecords.SingleLogRecord {
+
+ private final VibrationParamsRecords.Operation mOperation;
+ private final long mCreateTime;
+ private final int mTypesMask;
+ private final float mScale;
+
+ VibrationScaleParamRecord(VibrationParamsRecords.Operation operation, long createTime,
+ int typesMask, float scale) {
+ mOperation = operation;
+ mCreateTime = createTime;
+ mTypesMask = typesMask;
+ mScale = scale;
+ }
+
+ @Override
+ public int getGroupKey() {
+ return VibrationParam.scale;
+ }
+
+ @Override
+ public long getCreateUptimeMs() {
+ return mCreateTime;
+ }
+
+ @Override
+ public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) {
+ if (!(record instanceof VibrationScaleParamRecord param)) {
+ return false;
+ }
+ return mTypesMask == param.mTypesMask && mOperation == param.mOperation;
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw) {
+ String line = String.format(Locale.ROOT,
+ "%s | %6s | scale: %5s | typesMask: %6s | usages: %s",
+ DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)),
+ mOperation.name().toLowerCase(Locale.ROOT),
+ (mScale == NO_SCALE) ? "" : String.format(Locale.ROOT, "%.2f", mScale),
+ Long.toBinaryString(mTypesMask), createVibrationUsagesString());
+ pw.println(line);
+ }
+
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(VibrationParamProto.CREATE_TIME, mCreateTime);
+ proto.write(VibrationParamProto.IS_FROM_REQUEST,
+ mOperation == VibrationParamsRecords.Operation.PULL);
+
+ final long scaleToken = proto.start(VibrationParamProto.SCALE);
+ proto.write(VibrationScaleParamProto.TYPES_MASK, mTypesMask);
+ proto.write(VibrationScaleParamProto.SCALE, mScale);
+ proto.end(scaleToken);
+
+ proto.end(token);
+ }
+
+ private String createVibrationUsagesString() {
+ StringBuilder sb = new StringBuilder();
+ int[] usages = mapFromAdaptiveVibrationTypeToVibrationUsages(mTypesMask);
+ for (int i = 0; i < usages.length; i++) {
+ if (i > 0) sb.append(", ");
+ sb.append(VibrationAttributes.usageToString(usages[i]));
+ }
+ return sb.toString();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index f5d4d1e3926b..6710d02bee90 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -17,7 +17,6 @@
package com.android.server.vibrator;
import android.annotation.Nullable;
-import android.annotation.Nullable;
import android.hardware.vibrator.IVibrator;
import android.os.Binder;
import android.os.IVibratorStateListener;
@@ -354,13 +353,13 @@ final class VibratorController {
}
void dump(IndentingPrintWriter pw) {
- pw.println("VibratorController:");
+ pw.println("Vibrator (id=" + mVibratorInfo.getId() + "):");
pw.increaseIndent();
pw.println("isVibrating = " + mIsVibrating);
pw.println("isUnderExternalControl = " + mIsUnderExternalControl);
pw.println("currentAmplitude = " + mCurrentAmplitude);
pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful);
- pw.println("vibratorStateListenerCount = "
+ pw.println("vibratorStateListener size = "
+ mVibratorStateListeners.getRegisteredCallbackCount());
mVibratorInfo.dump(pw);
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
index 79a99b3ee2ff..b49fb85ecf3f 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
@@ -24,8 +24,6 @@ import android.util.Slog;
/**
* Holder class for {@link IVibratorController}.
- *
- * @hide
*/
public final class VibratorControllerHolder implements IBinder.DeathRecipient {
private static final String TAG = "VibratorControllerHolder";
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 78e0ebbb53fa..c1bf0393fc85 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -16,7 +16,6 @@
package com.android.server.vibrator;
-import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
@@ -84,7 +83,6 @@ import java.lang.ref.WeakReference;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
@@ -217,7 +215,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibrationSettings = new VibrationSettings(mContext, mHandler);
mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
- mVibratorControlService = new VibratorControlService(
+ mVibratorControlService = new VibratorControlService(mContext,
injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings,
mLock);
mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
@@ -416,14 +414,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override // Binder call
- public void performHapticFeedback(
- int uid, int deviceId, String opPkg, int constant, boolean always, String reason) {
+ public void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
+ boolean always, String reason, boolean fromIme) {
// Note that the `performHapticFeedback` method does not take a token argument from the
// caller, and instead, uses this service as the token. This is to mitigate performance
// impact that would otherwise be caused due to marshal latency. Haptic feedback effects are
// short-lived, so we don't need to cancel when the process dies.
performHapticFeedbackInternal(
- uid, deviceId, opPkg, constant, always, reason, /* token= */ this);
+ uid, deviceId, opPkg, constant, always, reason, /* token= */ this, fromIme);
}
/**
@@ -435,7 +433,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Nullable
HalVibration performHapticFeedbackInternal(
int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
- IBinder token) {
+ IBinder token, boolean fromIme) {
HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
if (hapticVibrationProvider == null) {
Slog.w(TAG, "performHapticFeedback; haptic vibration provider not ready.");
@@ -449,7 +447,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
CombinedVibration combinedVibration = CombinedVibration.createParallel(effect);
VibrationAttributes attrs =
hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
- constant, /* bypassVibrationIntensitySetting= */ always);
+ constant, /* bypassVibrationIntensitySetting= */ always, fromIme);
VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant);
return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, combinedVibration, attrs,
"performHapticFeedback: " + reason, token);
@@ -639,13 +637,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ " ");
synchronized (mLock) {
- pw.println("Vibrator Manager Service:");
+ pw.println("VibratorManagerService:");
pw.increaseIndent();
mVibrationSettings.dump(pw);
pw.println();
- pw.println("VibratorControllers:");
+ mVibrationScaler.dump(pw);
+ pw.println();
+
+ pw.println("Vibrators:");
pw.increaseIndent();
for (int i = 0; i < mVibrators.size(); i++) {
mVibrators.valueAt(i).dump(pw);
@@ -686,6 +687,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println();
pw.println();
mVibratorManagerRecords.dump(pw);
+
+ pw.println();
+ pw.println();
+ mVibratorControlService.dump(pw);
}
private void dumpProto(FileDescriptor fd) {
@@ -695,6 +700,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
synchronized (mLock) {
mVibrationSettings.dump(proto);
+ mVibrationScaler.dump(proto);
if (mCurrentVibration != null) {
mCurrentVibration.getVibration().getDebugInfo().dump(proto,
VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
@@ -716,6 +722,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
isUnderExternalControl);
}
mVibratorManagerRecords.dump(proto);
+ mVibratorControlService.dump(proto);
proto.flush();
}
@@ -887,7 +894,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (!vib.callerInfo.attrs.isFlagSet(
VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
// Scale resolves the default amplitudes from the effect before scaling them.
- vib.scaleEffects(mVibrationScaler::scale);
+ vib.scaleEffects(mVibrationScaler);
} else {
vib.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
@@ -1663,7 +1670,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
public Vibration.DebugInfo getDebugInfo() {
return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
- /* originalEffect= */ null, scale.scaleLevel, callerInfo);
+ /* originalEffect= */ null, scale.scaleLevel, scale.adaptiveHapticsScale,
+ callerInfo);
}
public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
@@ -1739,8 +1747,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
int aggregationTimeLimit) {
mAggregatedVibrationHistory =
new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit);
- mRecentVibrations = new VibrationRecords(
- recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
+ // Recent vibrations are not aggregated, to help debugging issues that just happened.
+ mRecentVibrations =
+ new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
}
synchronized void record(HalVibration vib) {
@@ -1752,9 +1761,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
private synchronized void record(Vibration.DebugInfo info) {
- AggregatedVibrationRecord removedRecord = mRecentVibrations.record(info);
- if (removedRecord != null) {
- mAggregatedVibrationHistory.record(removedRecord.mLatestVibration);
+ GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord =
+ mRecentVibrations.add(new VibrationRecord(info));
+ if (droppedRecord != null) {
+ // Move dropped record from recent list to aggregated history list.
+ mAggregatedVibrationHistory.add(droppedRecord.getLatest());
}
}
@@ -1763,9 +1774,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.increaseIndent();
mRecentVibrations.dump(pw);
pw.decreaseIndent();
+
pw.println();
pw.println();
-
pw.println("Aggregated vibration history:");
pw.increaseIndent();
mAggregatedVibrationHistory.dump(pw);
@@ -1778,127 +1789,75 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
/** Keep records of vibrations played and provide debug information for this service. */
- private static final class VibrationRecords {
- private final SparseArray<LinkedList<AggregatedVibrationRecord>> mVibrations =
- new SparseArray<>();
- private final int mSizeLimit;
- private final int mAggregationTimeLimit;
+ private static final class VibrationRecords
+ extends GroupedAggregatedLogRecords<VibrationRecord> {
VibrationRecords(int sizeLimit, int aggregationTimeLimit) {
- mSizeLimit = sizeLimit;
- mAggregationTimeLimit = aggregationTimeLimit;
+ super(sizeLimit, aggregationTimeLimit);
}
- synchronized AggregatedVibrationRecord record(Vibration.DebugInfo info) {
- int usage = info.mCallerInfo.attrs.getUsage();
- if (!mVibrations.contains(usage)) {
- mVibrations.put(usage, new LinkedList<>());
- }
- LinkedList<AggregatedVibrationRecord> records = mVibrations.get(usage);
- if (mAggregationTimeLimit > 0 && !records.isEmpty()) {
- AggregatedVibrationRecord lastRecord = records.getLast();
- if (lastRecord.mayAggregate(info, mAggregationTimeLimit)) {
- lastRecord.record(info);
- return null;
- }
- }
- AggregatedVibrationRecord removedRecord = null;
- if (records.size() > mSizeLimit) {
- removedRecord = records.removeFirst();
- }
- records.addLast(new AggregatedVibrationRecord(info));
- return removedRecord;
- }
-
- synchronized void dump(IndentingPrintWriter pw) {
- for (int i = 0; i < mVibrations.size(); i++) {
- pw.println(VibrationAttributes.usageToString(mVibrations.keyAt(i)) + ":");
- pw.increaseIndent();
- for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
- info.dump(pw);
- }
- pw.decreaseIndent();
- pw.println();
- }
- }
-
- synchronized void dump(ProtoOutputStream proto) {
- for (int i = 0; i < mVibrations.size(); i++) {
- long fieldId;
- switch (mVibrations.keyAt(i)) {
- case VibrationAttributes.USAGE_RINGTONE:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
- break;
- case VibrationAttributes.USAGE_NOTIFICATION:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
- break;
- case VibrationAttributes.USAGE_ALARM:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
- break;
- default:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
- }
- for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
- if (info.mLatestVibration.mPlayedEffect == null) {
- // External vibrations are reported separately in the dump proto
- info.dump(proto,
- VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
- } else {
- info.dump(proto, fieldId);
- }
- }
- }
+ @Override
+ void dumpGroupHeader(IndentingPrintWriter pw, int usage) {
+ pw.println(VibrationAttributes.usageToString(usage) + ":");
}
- synchronized void dumpOnSingleField(ProtoOutputStream proto, long fieldId) {
- for (int i = 0; i < mVibrations.size(); i++) {
- for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
- info.dump(proto, fieldId);
- }
- }
+ @Override
+ long findGroupKeyProtoFieldId(int usage) {
+ return switch (usage) {
+ case VibrationAttributes.USAGE_RINGTONE ->
+ VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
+ case VibrationAttributes.USAGE_NOTIFICATION ->
+ VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
+ case VibrationAttributes.USAGE_ALARM ->
+ VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
+ default ->
+ VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
+ };
}
}
/**
- * Record that keeps the last {@link Vibration.DebugInfo} played, aggregating close vibrations
- * from the same uid that have the same {@link VibrationAttributes} and {@link VibrationEffect}.
+ * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
+ * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
*/
- private static final class AggregatedVibrationRecord {
- private final Vibration.DebugInfo mFirstVibration;
- private Vibration.DebugInfo mLatestVibration;
- private int mVibrationCount;
+ private static final class VibrationRecord
+ implements GroupedAggregatedLogRecords.SingleLogRecord {
+ private final Vibration.DebugInfo mInfo;
- AggregatedVibrationRecord(Vibration.DebugInfo info) {
- mLatestVibration = mFirstVibration = info;
- mVibrationCount = 1;
+ VibrationRecord(Vibration.DebugInfo info) {
+ mInfo = info;
}
- synchronized boolean mayAggregate(Vibration.DebugInfo info, long timeLimit) {
- return Objects.equals(mLatestVibration.mCallerInfo.uid, info.mCallerInfo.uid)
- && Objects.equals(mLatestVibration.mCallerInfo.attrs, info.mCallerInfo.attrs)
- && Objects.equals(mLatestVibration.mPlayedEffect, info.mPlayedEffect)
- && Math.abs(mLatestVibration.mCreateTime - info.mCreateTime) < timeLimit;
+ @Override
+ public int getGroupKey() {
+ return mInfo.mCallerInfo.attrs.getUsage();
}
- synchronized void record(Vibration.DebugInfo vib) {
- mLatestVibration = vib;
- mVibrationCount++;
+ @Override
+ public long getCreateUptimeMs() {
+ return mInfo.mCreateTime;
}
- synchronized void dump(IndentingPrintWriter pw) {
- mFirstVibration.dumpCompact(pw);
- if (mVibrationCount == 1) {
- return;
- }
- if (mVibrationCount > 2) {
- pw.println(
- "-> Skipping " + (mVibrationCount - 2) + " aggregated vibrations, latest:");
+ @Override
+ public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) {
+ if (!(record instanceof VibrationRecord)) {
+ return false;
}
- mLatestVibration.dumpCompact(pw);
+ Vibration.DebugInfo info = ((VibrationRecord) record).mInfo;
+ return mInfo.mCallerInfo.uid == info.mCallerInfo.uid
+ && Objects.equals(mInfo.mCallerInfo.attrs, info.mCallerInfo.attrs)
+ && Objects.equals(mInfo.mPlayedEffect, info.mPlayedEffect);
}
- synchronized void dump(ProtoOutputStream proto, long fieldId) {
- mLatestVibration.dump(proto, fieldId);
+ @Override
+ public void dump(IndentingPrintWriter pw) {
+ // Prints a compact version of each vibration request for dumpsys.
+ mInfo.dumpCompact(pw);
+ }
+
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ mInfo.dump(proto, fieldId);
}
}
@@ -2001,7 +1960,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override
public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
-
if (!hasExternalControlCapability()) {
return SCALE_MUTE;
}
@@ -2085,10 +2043,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
mCurrentExternalVibration = vibHolder;
vibHolder.linkToDeath();
- vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel(
- attrs.getUsage());
- vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale(
- attrs.getUsage());
+ vibHolder.scale.scaleLevel = mVibrationScaler.getScaleLevel(attrs.getUsage());
+ vibHolder.scale.adaptiveHapticsScale =
+ mVibrationScaler.getAdaptiveHapticsScale(attrs.getUsage());
}
if (waitForCompletion) {
@@ -2300,7 +2257,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(),
Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, constant,
/* always= */ commonOptions.force, /* reason= */ commonOptions.description,
- deathBinder);
+ deathBinder, false /* fromIme */);
maybeWaitOnVibration(vib, commonOptions);
return 0;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 601c7f450d4f..5175b74116f4 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -39,6 +39,7 @@ import android.util.SparseArray;
import android.view.DisplayInfo;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.utils.TimingsTraceAndSlog;
import libcore.io.IoUtils;
@@ -65,7 +66,7 @@ public class WallpaperCropper {
* Maximum acceptable parallax.
* A value of 1 means "the additional width for parallax is at most 100% of the screen width"
*/
- private static final float MAX_PARALLAX = 1f;
+ @VisibleForTesting static final float MAX_PARALLAX = 1f;
/**
* We define three ways to adjust a crop. These modes are used depending on the situation:
@@ -73,10 +74,9 @@ public class WallpaperCropper {
* - When going from folded to unfolded, we want to add content
* - For a screen rotation, we want to keep the same amount of content
*/
- private static final int ADD = 1;
- private static final int REMOVE = 2;
- private static final int BALANCE = 3;
-
+ @VisibleForTesting static final int ADD = 1;
+ @VisibleForTesting static final int REMOVE = 2;
+ @VisibleForTesting static final int BALANCE = 3;
private final WallpaperDisplayHelper mWallpaperDisplayHelper;
@@ -131,19 +131,23 @@ public class WallpaperCropper {
(bitmapSize.y - crop.height()) / 2);
return crop;
}
+
+ // If any suggested crop is invalid, fallback to case 1
+ for (int i = 0; i < suggestedCrops.size(); i++) {
+ Rect testCrop = suggestedCrops.valueAt(i);
+ if (testCrop == null || testCrop.left < 0 || testCrop.top < 0
+ || testCrop.right > bitmapSize.x || testCrop.bottom > bitmapSize.y) {
+ Slog.w(TAG, "invalid crop: " + testCrop + " for bitmap size: " + bitmapSize);
+ return getCrop(displaySize, bitmapSize, new SparseArray<>(), rtl);
+ }
+ }
+
int orientation = getOrientation(displaySize);
// Case 2: if the orientation exists in the suggested crops, adjust the suggested crop
Rect suggestedCrop = suggestedCrops.get(orientation);
if (suggestedCrop != null) {
- if (suggestedCrop.left < 0 || suggestedCrop.top < 0
- || suggestedCrop.right > bitmapSize.x || suggestedCrop.bottom > bitmapSize.y) {
- Slog.w(TAG, "invalid suggested crop: " + suggestedCrop);
- Rect fullImage = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
- return getAdjustedCrop(fullImage, bitmapSize, displaySize, true, rtl, ADD);
- } else {
return getAdjustedCrop(suggestedCrop, bitmapSize, displaySize, true, rtl, ADD);
- }
}
// Case 3: if we have the 90° rotated orientation in the suggested crops, reuse it and
@@ -209,7 +213,8 @@ public class WallpaperCropper {
* Given a crop, a displaySize for the orientation of that crop, compute the visible part of the
* crop. This removes any additional width used for parallax. No-op if displaySize == null.
*/
- private static Rect noParallax(Rect crop, Point displaySize, Point bitmapSize, boolean rtl) {
+ @VisibleForTesting
+ static Rect noParallax(Rect crop, Point displaySize, Point bitmapSize, boolean rtl) {
if (displaySize == null) return crop;
Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD);
// only keep the visible part (without parallax)
@@ -240,12 +245,14 @@ public class WallpaperCropper {
* </li>
* </ul>
*/
- private static Rect getAdjustedCrop(Rect crop, Point bitmapSize, Point screenSize,
+ @VisibleForTesting
+ static Rect getAdjustedCrop(Rect crop, Point bitmapSize, Point screenSize,
boolean parallax, boolean rtl, int mode) {
Rect adjustedCrop = new Rect(crop);
float cropRatio = ((float) crop.width()) / crop.height();
float screenRatio = ((float) screenSize.x) / screenSize.y;
- if (cropRatio >= screenRatio) {
+ if (cropRatio == screenRatio) return crop;
+ if (cropRatio > screenRatio) {
if (!parallax) {
// rotate everything 90 degrees clockwise, compute the result, and rotate back
int newLeft = bitmapSize.y - crop.bottom;
@@ -274,6 +281,7 @@ public class WallpaperCropper {
}
}
} else {
+ // TODO (b/281648899) the third case is not always correct, fix that.
int widthToAdd = mode == REMOVE ? 0
: mode == ADD ? (int) (0.5 + crop.height() * screenRatio - crop.width())
: (int) (0.5 + crop.height() - crop.width());
@@ -644,6 +652,9 @@ public class WallpaperCropper {
if (!success) {
Slog.e(TAG, "Unable to apply new wallpaper");
wallpaper.getCropFile().delete();
+ wallpaper.mCropHints.clear();
+ wallpaper.cropHint.set(0, 0, 0, 0);
+ wallpaper.mSampleSize = 1f;
}
if (wallpaper.getCropFile().exists()) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index 88e9672cd0a1..0165d65283dc 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
@@ -341,6 +341,7 @@ public class WallpaperDataParser {
} else {
wallpaper.cropHint.set(totalCropHint);
}
+ wallpaper.mSampleSize = parser.getAttributeFloat(null, "sampleSize", 1f);
} else {
wallpaper.cropHint.set(totalCropHint);
}
@@ -493,6 +494,7 @@ public class WallpaperDataParser {
out.attributeInt(null, "totalCropTop", wallpaper.cropHint.top);
out.attributeInt(null, "totalCropRight", wallpaper.cropHint.right);
out.attributeInt(null, "totalCropBottom", wallpaper.cropHint.bottom);
+ out.attributeFloat(null, "sampleSize", wallpaper.mSampleSize);
} else if (!multiCrop()) {
final DisplayData wpdData =
mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index e9c40964aee4..043470f62850 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -30,6 +30,7 @@ import android.os.PatternMatcher;
import android.os.Process;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
@@ -37,6 +38,7 @@ import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
import com.android.internal.util.DumpUtils;
+import com.android.modules.expresslog.Histogram;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -52,6 +54,14 @@ public class WebViewUpdateService extends SystemService {
private static final String TAG = "WebViewUpdateService";
+ private static final Histogram sPrepareWebViewInSystemServerLatency = new Histogram(
+ "webview.value_prepare_webview_in_system_server_latency",
+ new Histogram.ScaledRangeOptions(20, 0, 1, 1.5f));
+
+ private static final Histogram sAppWaitingForRelroCompletionDelay = new Histogram(
+ "webview.value_app_waiting_for_relro_completion_delay",
+ new Histogram.ScaledRangeOptions(20, 0, 1, 1.4f));
+
private BroadcastReceiver mWebViewUpdatedReceiver;
private WebViewUpdateServiceInterface mImpl;
@@ -132,7 +142,10 @@ public class WebViewUpdateService extends SystemService {
}
public void prepareWebViewInSystemServer() {
+ long currentTimeMs = SystemClock.uptimeMillis();
mImpl.prepareWebViewInSystemServer();
+ sPrepareWebViewInSystemServerLatency.logSample(
+ (float) (SystemClock.uptimeMillis() - currentTimeMs));
}
private static String packageNameFromIntent(Intent intent) {
@@ -204,8 +217,12 @@ public class WebViewUpdateService extends SystemService {
throw new IllegalStateException("Cannot create a WebView from the SystemServer");
}
+ long startTimeMs = SystemClock.uptimeMillis();
final WebViewProviderResponse webViewProviderResponse =
WebViewUpdateService.this.mImpl.waitForAndGetProvider();
+ long endTimeMs = SystemClock.uptimeMillis();
+ sAppWaitingForRelroCompletionDelay.logSample((float) (endTimeMs - startTimeMs));
+
if (webViewProviderResponse.packageInfo != null) {
grantVisibilityToCaller(
webViewProviderResponse.packageInfo.packageName, Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 1d6ad6d3a6d9..532ff984ae56 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -23,12 +23,15 @@ import android.content.pm.Signature;
import android.os.AsyncTask;
import android.os.Trace;
import android.os.UserHandle;
+import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
+import com.android.modules.expresslog.Counter;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -357,6 +360,12 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
mNumRelroCreationsFinished = 0;
mNumRelroCreationsStarted =
mSystemInterface.onWebViewProviderChanged(newPackage);
+ Counter.logIncrement("webview.value_on_webview_provider_changed_counter");
+ if (newPackage.packageName.equals(getDefaultWebViewPackage().packageName)) {
+ Counter.logIncrement(
+ "webview.value_on_webview_provider_changed_"
+ + "with_default_package_counter");
+ }
// If the relro creations finish before we know the number of started creations
// we will have to do any cleanup/notifying here.
checkIfRelrosDoneLocked();
@@ -388,9 +397,15 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
@Override
public WebViewProviderInfo getDefaultWebViewPackage() {
- throw new IllegalStateException(
- "getDefaultWebViewPackage shouldn't be called if update_service_v2 flag is"
- + " disabled.");
+ for (WebViewProviderInfo provider : getWebViewPackages()) {
+ if (provider.availableByDefault) {
+ return provider;
+ }
+ }
+
+ // This should be unreachable because the config parser enforces that there is at least
+ // one availableByDefault provider.
+ throw new AndroidRuntimeException("No available by default WebView Provider.");
}
private static class ProviderAndPackageInfo {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 596de686089e..fb338ba245e1 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -31,6 +31,8 @@ import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
+import com.android.modules.expresslog.Counter;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -96,6 +98,9 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
private boolean mWebViewPackageDirty = false;
private boolean mAnyWebViewInstalled = false;
+ // Keeps track of whether we attempted to repair WebView before.
+ private boolean mAttemptedToRepairBefore = false;
+
private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
// The WebView package currently in use (or the one we are preparing).
@@ -136,6 +141,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
boolean removedOrChangedOldPackage = false;
String oldProviderName = null;
PackageInfo newPackage = null;
+ boolean repairNeeded = false;
synchronized (mLock) {
try {
newPackage = findPreferredWebViewPackage();
@@ -161,6 +167,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
Slog.e(TAG, "Could not find valid WebView package to create relro with "
+ e);
}
+ repairNeeded = shouldTriggerRepairLocked();
}
if (updateWebView && !removedOrChangedOldPackage
&& oldProviderName != null) {
@@ -170,12 +177,18 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
// only kills dependents of packages that are being removed.
mSystemInterface.killPackageDependents(oldProviderName);
}
+ if (repairNeeded) {
+ attemptRepair();
+ }
return;
}
}
}
private boolean shouldTriggerRepairLocked() {
+ if (mAttemptedToRepairBefore) {
+ return false;
+ }
if (mCurrentWebViewPackage == null) {
return true;
}
@@ -189,6 +202,26 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
}
}
+ private void attemptRepair() {
+ // We didn't find a valid WebView implementation. Try explicitly re-installing and
+ // re-enabling the default package for all users in case it was disabled. If this actually
+ // changes the state, we will see the PackageManager broadcast shortly and try again.
+ synchronized (mLock) {
+ if (mAttemptedToRepairBefore) {
+ return;
+ }
+ mAttemptedToRepairBefore = true;
+ }
+ Slog.w(
+ TAG,
+ "No provider available for all users, trying to install and enable "
+ + mDefaultProvider.packageName);
+ mSystemInterface.installExistingPackageForAllUsers(
+ mContext, mDefaultProvider.packageName);
+ mSystemInterface.enablePackageForAllUsers(
+ mContext, mDefaultProvider.packageName, true);
+ }
+
@Override
public void prepareWebViewInSystemServer() {
try {
@@ -211,18 +244,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
}
if (repairNeeded) {
- // We didn't find a valid WebView implementation. Try explicitly re-installing and
- // re-enabling the default package for all users in case it was disabled, even if we
- // already did the one-time migration before. If this actually changes the state, we
- // will see the PackageManager broadcast shortly and try again.
- Slog.w(
- TAG,
- "No provider available for all users, trying to install and enable "
- + mDefaultProvider.packageName);
- mSystemInterface.installExistingPackageForAllUsers(
- mContext, mDefaultProvider.packageName);
- mSystemInterface.enablePackageForAllUsers(
- mContext, mDefaultProvider.packageName, true);
+ attemptRepair();
}
} catch (Throwable t) {
@@ -332,6 +354,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
PackageInfo oldPackage = null;
PackageInfo newPackage = null;
boolean providerChanged = false;
+ boolean repairNeeded = false;
synchronized (mLock) {
oldPackage = mCurrentWebViewPackage;
@@ -354,11 +377,19 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
if (providerChanged) {
onWebViewProviderChanged(newPackage);
}
+ // Choosing another provider shouldn't break our state. Only check if repair
+ // is needed if this function is called as a result of a user change.
+ if (newProviderName == null) {
+ repairNeeded = shouldTriggerRepairLocked();
+ }
}
// Kill apps using the old provider only if we changed provider
if (providerChanged && oldPackage != null) {
mSystemInterface.killPackageDependents(oldPackage.packageName);
}
+ if (repairNeeded) {
+ attemptRepair();
+ }
// Return the new provider, this is not necessarily the one we were asked to switch to,
// but the persistent setting will now be pointing to the provider we were asked to
// switch to anyway.
@@ -383,6 +414,12 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
mNumRelroCreationsFinished = 0;
mNumRelroCreationsStarted =
mSystemInterface.onWebViewProviderChanged(newPackage);
+ Counter.logIncrement("webview.value_on_webview_provider_changed_counter");
+ if (newPackage.packageName.equals(getDefaultWebViewPackage().packageName)) {
+ Counter.logIncrement(
+ "webview.value_on_webview_provider_changed_"
+ + "with_default_package_counter");
+ }
// If the relro creations finish before we know the number of started creations
// we will have to do any cleanup/notifying here.
checkIfRelrosDoneLocked();
@@ -450,6 +487,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
* for all users, otherwise use the default provider.
*/
private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
+ Counter.logIncrement("webview.value_find_preferred_webview_package_counter");
// If the user has chosen provider, use that (if it's installed and enabled for all
// users).
String userChosenPackageName = mSystemInterface.getUserChosenWebViewProvider(mContext);
@@ -479,12 +517,15 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(mDefaultProvider);
if (validityResult(mDefaultProvider, packageInfo) == VALIDITY_OK) {
return packageInfo;
+ } else {
+ Counter.logIncrement("webview.value_default_webview_package_invalid_counter");
}
} catch (NameNotFoundException e) {
Slog.w(TAG, "Default WebView package (" + mDefaultProvider.packageName + ") not found");
}
// This should never happen during normal operation (only with modified system images).
+ Counter.logIncrement("webview.value_webview_not_usable_for_all_users_counter");
mAnyWebViewInstalled = false;
throw new WebViewPackageMissingException("Could not find a loadable WebView package");
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index d08e272d76dd..418998870f16 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -817,14 +817,9 @@ final class AccessibilityController {
switch (transition) {
case WindowManagerPolicy.TRANSIT_ENTER:
case WindowManagerPolicy.TRANSIT_SHOW: {
- if (!isMagnifierActivated) {
+ if (!isMagnifierActivated || !windowState.shouldMagnify()) {
break;
}
- if (Flags.doNotCheckIntersectionWhenNonMagnifiableWindowTransitions()) {
- if (!windowState.shouldMagnify()) {
- break;
- }
- }
switch (type) {
case WindowManager.LayoutParams.TYPE_APPLICATION:
case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
@@ -1214,6 +1209,7 @@ final class AccessibilityController {
private boolean mShown;
private boolean mLastSurfaceShown;
private int mAlpha;
+ private int mPreviousAlpha;
private volatile boolean mInvalidated;
@@ -1349,6 +1345,7 @@ final class AccessibilityController {
// using WindowManagerGlobalLock. Grab copies of these values before
// drawing on the canvas so that drawing can be performed outside of the lock.
int alpha;
+ boolean redrawBounds;
Rect drawingRect = null;
Region drawingBounds = null;
synchronized (mService.mGlobalLock) {
@@ -1366,7 +1363,13 @@ final class AccessibilityController {
mInvalidated = false;
alpha = mAlpha;
- if (alpha > 0) {
+ // For b/325863281, we should ensure the drawn border path is cleared when
+ // alpha = 0. Therefore, we cache the last used alpha when drawing as
+ // mPreviousAlpha and check it here. If mPreviousAlpha > 0, which means
+ // the border is showing now, then we should still redraw the clear path
+ // on the canvas so the border is cleared.
+ redrawBounds = mAlpha > 0 || mPreviousAlpha > 0;
+ if (redrawBounds) {
drawingBounds = new Region(mBounds);
// Empty dirty rectangle means unspecified.
if (mDirtyRect.isEmpty()) {
@@ -1383,7 +1386,7 @@ final class AccessibilityController {
final boolean showSurface;
// Draw without holding WindowManagerGlobalLock.
- if (alpha > 0) {
+ if (redrawBounds) {
Canvas canvas = null;
try {
canvas = mSurface.lockCanvas(drawingRect);
@@ -1397,11 +1400,11 @@ final class AccessibilityController {
mPaint.setAlpha(alpha);
canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
mSurface.unlockCanvasAndPost(canvas);
- showSurface = true;
- } else {
- showSurface = false;
+ mPreviousAlpha = alpha;
}
+ showSurface = alpha > 0;
+
if (showSurface && !mLastSurfaceShown) {
mTransaction.show(mSurfaceControl).apply();
mLastSurfaceShown = true;
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ed5df5fab017..981c4c078dd1 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -506,7 +506,8 @@ class ActivityClientController extends IActivityClientController.Stub {
// keep backwards compatibility we remove the task from recents when finishing
// task with root activity.
mTaskSupervisor.removeTask(tr, false /*killProcess*/,
- finishWithRootActivity, "finish-activity", r.getUid(), r.info.name);
+ finishWithRootActivity, "finish-activity", r.getUid(), r.getPid(),
+ r.info.name);
res = true;
// Explicitly dismissing the activity so reset its relaunch flag.
r.mRelaunchReason = RELAUNCH_REASON_NONE;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 90cff3950047..d30a2167a183 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -553,7 +553,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean launchFailed; // set if a launched failed, to abort on 2nd try
boolean delayedResume; // not yet resumed because of stopped app switches?
boolean finishing; // activity in pending finish list?
- int configChangeFlags; // which config values have changed
private boolean keysPaused; // has key dispatching been paused for it?
int launchMode; // the launch mode activity attribute.
int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
@@ -1295,10 +1294,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mDeferHidingClient) {
pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient);
}
- if (configChangeFlags != 0) {
- pw.print(prefix); pw.print(" configChangeFlags=");
- pw.println(Integer.toHexString(configChangeFlags));
- }
if (mServiceConnectionsHolder != null) {
pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
}
@@ -1476,7 +1471,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- private void scheduleConfigurationChanged(Configuration config) {
+ private void scheduleConfigurationChanged(@NonNull Configuration config,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
if (!attachedToProcess()) {
ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration "
+ "update - client not running, activityRecord=%s", this);
@@ -1487,7 +1483,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ "config: %s", this, config);
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- ActivityConfigurationChangeItem.obtain(token, config));
+ ActivityConfigurationChangeItem.obtain(token, config, activityWindowInfo));
} catch (RemoteException e) {
// If process died, whatever.
}
@@ -2893,6 +2889,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Makes starting window always fill the associated task. */
private void attachStartingSurfaceToAssociatedTask() {
+ if (mSyncState == SYNC_STATE_NONE && isEmbedded()) {
+ // Collect this activity since it's starting window will reparent to task. To ensure
+ // any starting window's transaction will occur in order.
+ mTransitionController.collect(this);
+ }
// Associate the configuration of starting window with the task.
overrideConfigurationPropagation(mStartingWindow, mStartingData.mAssociatedTask);
getSyncTransaction().reparent(mStartingWindow.mSurfaceControl,
@@ -4058,7 +4059,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- DestroyActivityItem.obtain(token, finishing, configChangeFlags));
+ DestroyActivityItem.obtain(token, finishing));
} catch (Exception e) {
// We can just ignore exceptions here... if the process has crashed, our death
// notification will clean things up.
@@ -4100,8 +4101,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- configChangeFlags = 0;
-
return removedFromHistory;
}
@@ -5020,7 +5019,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return PauseActivityItem.obtain(token);
case STOPPING:
case STOPPED:
- return StopActivityItem.obtain(token, configChangeFlags);
+ return StopActivityItem.obtain(token);
default:
// Do not send a result immediately if the activity is in state INITIALIZING,
// RESTARTING_PROCESS, FINISHING, DESTROYING, or DESTROYED.
@@ -6294,7 +6293,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
try {
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
PauseActivityItem.obtain(token, finishing, false /* userLeaving */,
- configChangeFlags, false /* dontReport */, mAutoEnteringPip));
+ false /* dontReport */, mAutoEnteringPip));
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
}
@@ -6608,7 +6607,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
EventLogTags.writeWmStopActivity(
mUserId, System.identityHashCode(this), shortComponentName);
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- StopActivityItem.obtain(token, configChangeFlags));
+ StopActivityItem.obtain(token));
mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
} catch (Exception e) {
@@ -6866,6 +6865,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// stop tracking
mSplashScreenStyleSolidColor = true;
+ mAtmService.mBackNavigationController.removePredictiveSurfaceIfNeeded(this);
if (mStartingWindow != null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s"
+ ": first real window is shown, no animation", win.mToken);
@@ -9785,7 +9785,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// configurations because there are cases (like moving a task to the root pinned task) where
// the combine configurations are equal, but would otherwise differ in the override config
mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
- if (getConfiguration().equals(mTmpConfig) && !displayChanged) {
+ final ActivityWindowInfo newActivityWindowInfo = getActivityWindowInfo();
+ final boolean isActivityWindowInfoChanged = Flags.activityWindowInfoFlag()
+ && !mLastReportedActivityWindowInfo.equals(newActivityWindowInfo);
+ if (!displayChanged && !isActivityWindowInfoChanged
+ && getConfiguration().equals(mTmpConfig)) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+ "unchanged in %s", this);
return true;
@@ -9800,7 +9804,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- final ActivityWindowInfo newActivityWindowInfo = getActivityWindowInfo();
setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
setLastReportedActivityWindowInfo(newActivityWindowInfo);
@@ -9823,7 +9826,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig,
newActivityWindowInfo);
} else {
- scheduleConfigurationChanged(newMergedOverrideConfig);
+ scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
}
notifyDisplayCompatPolicyAboutConfigurationChange(
mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
@@ -9848,7 +9851,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (shouldRelaunchLocked(changes, mTmpConfig)) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
- configChangeFlags |= changes;
if (mVisible && mAtmService.mTmpUpdateConfigurationResult.mIsUpdating
&& !mTransitionController.isShellTransitionsEnabled()) {
startFreezingScreenLocked(app, mAtmService.mTmpUpdateConfigurationResult.changes);
@@ -9877,7 +9879,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STATES, "Config is relaunching invisible "
+ "activity %s called by %s", this, Debug.getCallers(4));
}
- relaunchActivityLocked(preserveWindow);
+ relaunchActivityLocked(preserveWindow, changes);
// All done... tell the caller we weren't able to keep this activity around.
return false;
@@ -9891,7 +9893,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig,
newActivityWindowInfo);
} else {
- scheduleConfigurationChanged(newMergedOverrideConfig);
+ scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
}
notifyDisplayCompatPolicyAboutConfigurationChange(
mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
@@ -10019,9 +10021,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
| CONFIG_SCREEN_LAYOUT)) != 0;
}
- void relaunchActivityLocked(boolean preserveWindow) {
+ void relaunchActivityLocked(boolean preserveWindow, int configChangeFlags) {
if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) {
- configChangeFlags = 0;
return;
}
if (!preserveWindow) {
@@ -10094,8 +10095,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The activity may be waiting for stop, but that is no longer appropriate for it.
mTaskSupervisor.mStoppingActivities.remove(this);
-
- configChangeFlags = 0;
}
/**
@@ -10164,7 +10163,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// {@link ActivityTaskManagerService.activityStopped}).
try {
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- StopActivityItem.obtain(token, 0 /* configChanges */));
+ StopActivityItem.obtain(token));
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown during restart " + this, e);
}
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 01d077a5bc55..4149bd9c6c56 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -41,7 +41,7 @@ class ActivitySecurityModelFeatureFlags {
static final String DOC_LINK = "go/android-asm";
/** Used to determine which version of the ASM logic was used in logs while we iterate */
- static final int ASM_VERSION = 9;
+ static final int ASM_VERSION = 10;
private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c137c54949e9..6ad056f5a902 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2089,7 +2089,7 @@ class ActivityStarter {
if (!mSupervisor.getBackgroundActivityLaunchController().checkActivityAllowedToStart(
mSourceRecord, r, newTask, avoidMoveToFront(), targetTask, mLaunchFlags, mBalCode,
- mCallingUid, mRealCallingUid)) {
+ mCallingUid, mRealCallingUid, mPreferredTaskDisplayArea)) {
return START_ABORTED;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 218b7512b861..e283f3e8ef0e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4163,19 +4163,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
enforceTaskPermission("onPictureInPictureUiStateChanged");
- // The PictureInPictureUiState is sent to current pip task if there is any
- // -or- the top standard task (state like entering PiP does not require a pinned task).
- final Task task;
- if (mRootWindowContainer.getDefaultTaskDisplayArea().hasPinnedTask()) {
- task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootPinnedTask();
- } else {
- task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootTask(
- t -> t.isActivityTypeStandard());
- }
- if (task != null && task.getTopMostActivity() != null
- && !task.getTopMostActivity().isState(FINISHING, DESTROYING, DESTROYED)) {
- mWindowManager.mAtmService.mActivityClientController.onPictureInPictureUiStateChanged(
- task.getTopMostActivity(), pipState);
+ synchronized (mGlobalLock) {
+ // The PictureInPictureUiState is sent to current pip task if there is any
+ // -or- the top standard task (state like entering PiP does not require a pinned task).
+ final Task task;
+ if (mRootWindowContainer.getDefaultTaskDisplayArea().hasPinnedTask()) {
+ task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootPinnedTask();
+ } else {
+ task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootTask(
+ t -> t.isActivityTypeStandard());
+ }
+ if (task != null && task.getTopMostActivity() != null
+ && !task.getTopMostActivity().isState(FINISHING, DESTROYING, DESTROYED)) {
+ mWindowManager.mAtmService.mActivityClientController
+ .onPictureInPictureUiStateChanged(task.getTopMostActivity(), pipState);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index aefa777a1db7..2cda1f55b038 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -42,6 +42,7 @@ import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.INVALID_PID;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -73,6 +74,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_TASK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.ClientLifecycleManager.shouldDispatchCompatClientTransactionIndependently;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -947,6 +949,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
// Schedule transaction.
+ if (shouldDispatchCompatClientTransactionIndependently(r.mTargetSdk)) {
+ // LaunchActivityItem has @UnsupportedAppUsage usages.
+ // Guard the bundleClientTransactionFlag feature with targetSDK on Android 15+.
+ // To not bundle the transaction, dispatch the pending before schedule new
+ // transaction.
+ mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread());
+ }
mService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
proc.getThread(), launchActivityItem, lifecycleItem,
// Immediately dispatch the transaction, so that if it fails, the server can
@@ -1652,11 +1661,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
* @return Returns true if the given task was found and removed.
*/
boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,
- String reason, int callingUid) {
+ String reason, int callingUid, int callingPid) {
final Task task =
mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task != null) {
- removeTask(task, killProcess, removeFromRecents, reason, callingUid, null);
+ removeTask(task, killProcess, removeFromRecents, reason, callingUid, callingPid, null);
return true;
}
Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
@@ -1664,11 +1673,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
- removeTask(task, killProcess, removeFromRecents, reason, SYSTEM_UID, null);
+ removeTask(task, killProcess, removeFromRecents, reason, SYSTEM_UID, INVALID_PID, null);
}
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason,
- int callingUid, String callerActivityClassName) {
+ int callingUid, int callingPid, String callerActivityClassName) {
if (task.mInRemoveTask) {
// Prevent recursion.
return;
@@ -1705,8 +1714,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
- mBalController
- .checkActivityAllowedToClearTask(task, callingUid, callerActivityClassName);
+ mBalController.checkActivityAllowedToClearTask(
+ task, callingUid, callingPid, callerActivityClassName);
} finally {
task.mInRemoveTask = false;
}
@@ -1874,7 +1883,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Task was trimmed from the recent tasks list -- remove the active task record as well
// since the user won't really be able to go back to it
removeTaskById(task.mTaskId, killProcess, false /* removeFromRecents */,
- "recent-task-trimmed", SYSTEM_UID);
+ "recent-task-trimmed", SYSTEM_UID, INVALID_PID);
}
task.removedFromRecents();
}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 50de0b08f3b0..d699af872c7f 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -77,11 +77,13 @@ class AppTaskImpl extends IAppTask.Stub {
synchronized (mService.mGlobalLock) {
int origCallingUid = Binder.getCallingUid();
+ int origCallingPid = Binder.getCallingPid();
final long callingIdentity = Binder.clearCallingIdentity();
try {
// We remove the task from recents to preserve backwards
if (!mService.mTaskSupervisor.removeTaskById(mTaskId, false,
- REMOVE_FROM_RECENTS, "finish-and-remove-task", origCallingUid)) {
+ REMOVE_FROM_RECENTS, "finish-and-remove-task", origCallingUid,
+ origCallingPid)) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
} finally {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b51f89931128..e15512678104 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -61,6 +61,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.wm.utils.InsetUtils;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -644,6 +645,10 @@ class BackNavigationController {
return false;
}
+ void removePredictiveSurfaceIfNeeded(ActivityRecord openActivity) {
+ mAnimationHandler.markWindowHasDrawn(openActivity);
+ }
+
private class NavigationMonitor {
// The window which triggering the back navigation.
private WindowState mNavigatingWindow;
@@ -897,7 +902,8 @@ class BackNavigationController {
mWindowManagerService = wms;
final Context context = wms.mContext;
mShowWindowlessSurface = context.getResources().getBoolean(
- com.android.internal.R.bool.config_predictShowStartingSurface);
+ com.android.internal.R.bool.config_predictShowStartingSurface)
+ && Flags.activitySnapshotByDefault();
}
private static final int UNKNOWN = 0;
private static final int TASK_SWITCH = 1;
@@ -1032,6 +1038,23 @@ class BackNavigationController {
return isAnimateTarget(wc, mCloseAdaptor.mTarget, mSwitchType);
}
+ void markWindowHasDrawn(ActivityRecord activity) {
+ if (!mComposed || mWaitTransition) {
+ return;
+ }
+ boolean allWindowDrawn = true;
+ for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) {
+ final BackWindowAnimationAdaptor next = mOpenAnimAdaptor.mAdaptors[i];
+ if (isAnimateTarget(activity, next.mTarget, mSwitchType)) {
+ next.mAppWindowDrawn = true;
+ }
+ allWindowDrawn &= next.mAppWindowDrawn;
+ }
+ if (allWindowDrawn) {
+ mOpenAnimAdaptor.cleanUpWindowlessSurface(true);
+ }
+ }
+
private static boolean isAnimateTarget(@NonNull WindowContainer window,
@NonNull WindowContainer animationTarget, int switchType) {
if (switchType == TASK_SWITCH) {
@@ -1134,15 +1157,17 @@ class BackNavigationController {
final BackWindowAnimationAdaptor adaptor =
new BackWindowAnimationAdaptor(target, isOpen, switchType);
final SurfaceControl.Transaction pt = target.getPendingTransaction();
- target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK);
// Workaround to show TaskFragment which can be hide in Transitions and won't show
// during isAnimating.
if (isOpen && target.asActivityRecord() != null) {
final TaskFragment fragment = target.asActivityRecord().getTaskFragment();
if (fragment != null) {
+ // Ensure task fragment surface has updated, in case configuration has changed.
+ fragment.updateOrganizedTaskFragmentSurface();
pt.show(fragment.mSurfaceControl);
}
}
+ target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK);
return adaptor;
}
@@ -1181,8 +1206,6 @@ class BackNavigationController {
for (int i = mAdaptors.length - 1; i >= 0; --i) {
mAdaptors[i].mTarget.cancelAnimation();
}
- mRequestedStartingSurfaceId = INVALID_TASK_ID;
- mStartingSurface = null;
if (mCloseTransaction != null) {
mCloseTransaction.apply();
mCloseTransaction = null;
@@ -1235,7 +1258,7 @@ class BackNavigationController {
represent.allowEnterPip);
}
- void createStartingSurface(ActivityRecord[] visibleOpenActivities) {
+ void createStartingSurface(@Nullable TaskSnapshot snapshot) {
if (mAdaptors[0].mSwitchType == DIALOG_CLOSE) {
return;
}
@@ -1253,7 +1276,6 @@ class BackNavigationController {
if (mainActivity == null) {
return;
}
- final TaskSnapshot snapshot = getSnapshot(mainOpen, visibleOpenActivities);
// If there is only one adaptor, attach the windowless window to top activity,
// because fixed rotation only applies on activity.
// Note that embedded activity won't use fixed rotation.
@@ -1321,11 +1343,13 @@ class BackNavigationController {
.removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
!openTransitionMatch);
mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ mStartingSurface = null;
}
}
private static class BackWindowAnimationAdaptor implements AnimationAdapter {
SurfaceControl mCapturedLeash;
+ boolean mAppWindowDrawn;
private final Rect mBounds = new Rect();
private final WindowContainer mTarget;
private final boolean mIsOpen;
@@ -1507,9 +1531,16 @@ class BackNavigationController {
private void applyPreviewStrategy(
@NonNull BackWindowAnimationAdaptorWrapper openAnimationAdaptor,
@NonNull ActivityRecord[] visibleOpenActivities) {
+ boolean needsLaunchBehind = true;
if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
- openAnimationAdaptor.createStartingSurface(visibleOpenActivities);
- } else {
+ final WindowContainer mainOpen = openAnimationAdaptor.mAdaptors[0].mTarget;
+ final TaskSnapshot snapshot = getSnapshot(mainOpen, visibleOpenActivities);
+ openAnimationAdaptor.createStartingSurface(snapshot);
+ // set LaunchBehind if we are creating splash screen surface.
+ needsLaunchBehind = snapshot == null
+ && openAnimationAdaptor.mRequestedStartingSurfaceId != INVALID_TASK_ID;
+ }
+ if (needsLaunchBehind) {
for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
setLaunchBehind(visibleOpenActivities[i]);
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4e28384c20be..071f40342461 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -25,9 +25,12 @@ import static android.app.ActivityOptions.BackgroundActivityStartMode;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+import static android.os.Process.INVALID_PID;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+import static com.android.server.wm.ActivityStarter.ASM_RESTRICTIONS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -67,6 +70,7 @@ import android.util.DebugUtils;
import android.util.Slog;
import android.widget.Toast;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
@@ -75,6 +79,7 @@ import com.android.server.UiThread;
import com.android.server.am.PendingIntentRecord;
import java.lang.annotation.Retention;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringJoiner;
import java.util.function.Consumer;
@@ -1022,7 +1027,7 @@ public class BackgroundActivityStartController {
}
/**
- * Log activity starts which violate one of the following rules of the
+ * Check activity starts which violate one of the following rules of the
* activity security model (ASM):
* See go/activity-security for rationale behind the rules.
* 1. Within a task, only an activity matching a top UID of the task can start activities
@@ -1032,7 +1037,7 @@ public class BackgroundActivityStartController {
boolean checkActivityAllowedToStart(@Nullable ActivityRecord sourceRecord,
@NonNull ActivityRecord targetRecord, boolean newTask, boolean avoidMoveTaskToFront,
@Nullable Task targetTask, int launchFlags, int balCode, int callingUid,
- int realCallingUid) {
+ int realCallingUid, TaskDisplayArea preferredTaskDisplayArea) {
// BAL Exception allowed in all cases
if (balCode == BAL_ALLOW_ALLOWLISTED_UID) {
return true;
@@ -1055,68 +1060,46 @@ public class BackgroundActivityStartController {
}
}
- if (balCode == BAL_ALLOW_GRACE_PERIOD) {
- // Allow if launching into new task, and caller matches most recently finished activity
- if (taskToFront && mTopFinishedActivity != null
- && mTopFinishedActivity.mUid == callingUid) {
- return true;
- }
-
- // Launching into existing task - allow if matches most recently finished activity
- // within the task.
- // We can reach here multiple ways:
- // 1. activity in fg fires intent (taskToFront = false, sourceRecord is available)
- // 2. activity in bg fires intent (taskToFront = false, sourceRecord is available)
- // 3. activity in bg fires intent with NEW_FLAG (taskToFront = true,
- // avoidMoveTaskToFront = true, sourceRecord is available)
- // 4. activity in bg fires PI (taskToFront = true, avoidMoveTaskToFront = true,
- // sourceRecord is not available, targetTask may be available)
- if (!taskToFront || avoidMoveTaskToFront) {
- if (targetTask != null) {
- FinishedActivityEntry finishedEntry =
- mTaskIdToFinishedActivity.get(targetTask.mTaskId);
- if (finishedEntry != null && finishedEntry.mUid == callingUid) {
- return true;
- }
- }
-
- if (sourceRecord != null) {
- FinishedActivityEntry finishedEntry =
- mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
- if (finishedEntry != null && finishedEntry.mUid == callingUid) {
- return true;
- }
- }
- }
- }
-
- BlockActivityStart bas = null;
+ BlockActivityStart bas = new BlockActivityStart();
if (sourceRecord != null) {
- boolean passesAsmChecks = true;
Task sourceTask = sourceRecord.getTask();
+ Task taskToCheck = taskToFront ? sourceTask : targetTask;
+ bas = checkTopActivityForAsm(taskToCheck, sourceRecord.getUid(),
+ sourceRecord, bas);
+
// Allow launching into a new task (or a task matching the launched activity's
// affinity) only if the current task is foreground or mutating its own task.
// The latter can happen eg. if caller uses NEW_TASK flag and the activity being
// launched matches affinity of source task.
- if (taskToFront) {
- passesAsmChecks = sourceTask != null
- && (sourceTask.isVisible() || sourceTask == targetTask);
- }
-
- if (passesAsmChecks) {
- Task taskToCheck = taskToFront ? sourceTask : targetTask;
- bas = isTopActivityMatchingUidAbsentForAsm(taskToCheck, sourceRecord.getUid(),
- sourceRecord);
+ if (taskToFront && bas.mTopActivityMatchesSource) {
+ bas.mTopActivityMatchesSource = (sourceTask != null
+ && (sourceTask.isVisible() || sourceTask == targetTask));
}
} else if (targetTask != null && (!taskToFront || avoidMoveTaskToFront)) {
// We don't have a sourceRecord, and we're launching into an existing task.
// Allow if callingUid is top of stack.
- bas = isTopActivityMatchingUidAbsentForAsm(targetTask, callingUid,
- /*sourceRecord*/null);
+ bas = checkTopActivityForAsm(targetTask, callingUid,
+ /*sourceRecord*/null, bas);
+ } else {
+ // We're launching from a non-visible activity. Has any visible app opted in?
+ TaskDisplayArea displayArea = targetTask != null && targetTask.getDisplayArea() != null
+ ? targetTask.getDisplayArea()
+ : preferredTaskDisplayArea;
+ if (displayArea != null) {
+ ArrayList<Task> visibleTasks = displayArea.getVisibleTasks();
+ for (int i = 0; i < visibleTasks.size(); i++) {
+ Task task = visibleTasks.get(i);
+ if (visibleTasks.size() == 1 && task.isActivityTypeHomeOrRecents()) {
+ bas.optedIn(task.getTopMostActivity());
+ } else {
+ bas = checkTopActivityForAsm(task, callingUid, /*sourceRecord*/null, bas);
+ }
+ }
+ }
}
- if (bas != null && !bas.mWouldBlockActivityStartIgnoringFlag) {
+ if (bas.mTopActivityMatchesSource) {
return true;
}
@@ -1140,13 +1123,16 @@ public class BackgroundActivityStartController {
? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_SAME_TASK
: FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_DIFFERENT_TASK);
- boolean blockActivityStartAndFeatureEnabled = ActivitySecurityModelFeatureFlags
- .shouldRestrictActivitySwitch(callingUid)
- && (bas == null || bas.mBlockActivityStartIfFlagEnabled);
+ boolean enforceBlock = bas.mTopActivityOptedIn
+ && ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(callingUid);
+
+ boolean allowedByGracePeriod = allowedByAsmGracePeriod(callingUid, sourceRecord, targetTask,
+ balCode, taskToFront, avoidMoveTaskToFront);
String asmDebugInfo = getDebugInfoForActivitySecurity("Launch", sourceRecord,
targetRecord, targetTask, targetTopActivity, realCallingUid, balCode,
- blockActivityStartAndFeatureEnabled, taskToFront, avoidMoveTaskToFront);
+ enforceBlock, taskToFront, avoidMoveTaskToFront, allowedByGracePeriod,
+ bas.mActivityOptedIn);
FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
/* caller_uid */
@@ -1184,7 +1170,7 @@ public class BackgroundActivityStartController {
String launchedFromPackageName = targetRecord.launchedFromPackage;
if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
String toastText = ActivitySecurityModelFeatureFlags.DOC_LINK
- + (blockActivityStartAndFeatureEnabled ? " blocked " : " would block ")
+ + (enforceBlock ? " blocked " : " would block ")
+ getApplicationLabel(mService.mContext.getPackageManager(),
launchedFromPackageName);
showToast(toastText);
@@ -1192,7 +1178,7 @@ public class BackgroundActivityStartController {
Slog.i(TAG, asmDebugInfo);
}
- if (blockActivityStartAndFeatureEnabled) {
+ if (enforceBlock) {
Slog.e(TAG, "[ASM] Abort Launching r: " + targetRecord
+ " as source: "
+ (sourceRecord != null ? sourceRecord : launchedFromPackageName)
@@ -1251,18 +1237,18 @@ public class BackgroundActivityStartController {
// Find the first activity which matches a safe UID and is not finishing. Clear everything
// above it
+ int[] finishCount = new int[1];
boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
.shouldRestrictActivitySwitch(callingUid);
- int[] finishCount = new int[0];
- if (shouldBlockActivityStart
- && blockCrossUidActivitySwitchFromBelowForActivity(targetTaskTop)) {
+ BlockActivityStart bas = checkCrossUidActivitySwitchFromBelow(
+ targetTaskTop, callingUid, new BlockActivityStart());
+ if (shouldBlockActivityStart && bas.mTopActivityOptedIn) {
ActivityRecord activity = targetTask.getActivity(isLaunchingOrLaunched);
if (activity == null) {
// mStartActivity is not in task, so clear everything
activity = targetRecord;
}
- finishCount = new int[1];
targetTask.performClearTop(activity, launchFlags, finishCount);
if (finishCount[0] > 0) {
Slog.w(TAG, "Cleared top n: " + finishCount[0] + " activities from task t: "
@@ -1279,7 +1265,8 @@ public class BackgroundActivityStartController {
Slog.i(TAG, getDebugInfoForActivitySecurity("Clear Top", sourceRecord, targetRecord,
targetTask, targetTaskTop, realCallingUid, balCode, shouldBlockActivityStart,
- /* taskToFront */ true, /* avoidMoveTaskToFront */ false));
+ /* taskToFront */ true, /* avoidMoveTaskToFront */ false,
+ /* allowedByAsmGracePeriod */ false, bas.mActivityOptedIn));
}
}
@@ -1287,7 +1274,7 @@ public class BackgroundActivityStartController {
* Returns home if the passed in callingUid is not top of the stack, rather than returning to
* previous task.
*/
- void checkActivityAllowedToClearTask(@NonNull Task task, int callingUid,
+ void checkActivityAllowedToClearTask(@NonNull Task task, int callingUid, int callingPid,
@NonNull String callerActivityClassName) {
// We may have already checked that the callingUid has additional clearTask privileges, and
// cleared the calling identify. If so, we infer we do not need further restrictions here.
@@ -1295,14 +1282,28 @@ public class BackgroundActivityStartController {
return;
}
+ String packageName = mService.mContext.getPackageManager().getNameForUid(callingUid);
+ BalState state = new BalState(callingUid, callingPid, packageName, INVALID_UID,
+ INVALID_PID, null, null, null, null, null, ActivityOptions.makeBasic());
+ @BalCode int balCode = checkBackgroundActivityStartAllowedByCaller(state).mCode;
+ if (balCode == BAL_ALLOW_ALLOWLISTED_UID
+ || balCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
+ || balCode == BAL_ALLOW_PERMISSION
+ || balCode == BAL_ALLOW_SAW_PERMISSION
+ || balCode == BAL_ALLOW_VISIBLE_WINDOW
+ || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW) {
+ return;
+ }
+
TaskDisplayArea displayArea = task.getTaskDisplayArea();
if (displayArea == null) {
// If there is no associated display area, we can not return home.
return;
}
- BlockActivityStart bas = isTopActivityMatchingUidAbsentForAsm(task, callingUid, null);
- if (!bas.mWouldBlockActivityStartIgnoringFlag) {
+ BlockActivityStart bas = checkTopActivityForAsm(task, callingUid, null,
+ new BlockActivityStart());
+ if (bas.mTopActivityMatchesSource) {
return;
}
@@ -1339,8 +1340,7 @@ public class BackgroundActivityStartController {
);
boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
- .shouldRestrictActivitySwitch(callingUid)
- && bas.mBlockActivityStartIfFlagEnabled;
+ .shouldRestrictActivitySwitch(callingUid) && bas.mTopActivityOptedIn;
PackageManager pm = mService.mContext.getPackageManager();
String callingPackage = pm.getNameForUid(callingUid);
@@ -1381,32 +1381,30 @@ public class BackgroundActivityStartController {
* <p>
* The 'sourceRecord' can be considered top even if it is 'finishing'
* <p>
- * Returns a class where the elements are:
- * <pre>
- * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
- * consideration feature flag and targetSdk).
- * wouldBlockActivityStartIgnoringFlags: {@code true} if we should warn about the transition via
- * toasts. This happens if the transition would be blocked in case both the app was targeting V+
- * and the feature was enabled.
- * </pre>
*/
- private BlockActivityStart isTopActivityMatchingUidAbsentForAsm(@NonNull Task task,
- int uid, @Nullable ActivityRecord sourceRecord) {
+ private BlockActivityStart checkTopActivityForAsm(@NonNull Task task,
+ int uid, @Nullable ActivityRecord sourceRecord, BlockActivityStart bas) {
// If the source is visible, consider it 'top'.
if (sourceRecord != null && sourceRecord.isVisibleRequested()) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ return bas.matchesSource();
}
- // Always allow actual top activity to clear task
+ // Always allow actual top activity
ActivityRecord topActivity = task.getTopMostActivity();
- if (topActivity != null && topActivity.isUid(uid)) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ if (topActivity == null) {
+ Slog.wtf(TAG, "Activities for task: " + task + " not found.");
+ return bas.optedIn(topActivity);
+ }
+
+ bas = checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
+ if (bas.mTopActivityMatchesSource) {
+ return bas;
}
// If UID is visible in target task, allow launch
if (task.forAllActivities((Predicate<ActivityRecord>)
ar -> ar.isUid(uid) && ar.isVisibleRequested())) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ return bas.matchesSource();
}
// Consider the source activity, whether or not it is finishing. Do not consider any other
@@ -1417,82 +1415,91 @@ public class BackgroundActivityStartController {
// Check top of stack (or the first task fragment for embedding).
topActivity = task.getActivity(topOfStackPredicate);
if (topActivity == null) {
- return new BlockActivityStart(true, true);
+ return bas;
}
- BlockActivityStart pair = blockCrossUidActivitySwitchFromBelow(topActivity, uid);
- if (!pair.mBlockActivityStartIfFlagEnabled) {
- return pair;
+ bas = checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
+ if (bas.mTopActivityMatchesSource) {
+ return bas;
}
// Even if the top activity is not a match, we may be in an embedded activity scenario with
// an adjacent task fragment. Get the second fragment.
TaskFragment taskFragment = topActivity.getTaskFragment();
if (taskFragment == null) {
- return pair;
+ return bas;
}
TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
if (adjacentTaskFragment == null) {
- return pair;
+ return bas;
}
// Check the second fragment.
topActivity = adjacentTaskFragment.getActivity(topOfStackPredicate);
if (topActivity == null) {
- return new BlockActivityStart(true, true);
+ return bas;
}
- return blockCrossUidActivitySwitchFromBelow(topActivity, uid);
+ return checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
}
/**
* Determines if a source is allowed to add or remove activities from the task,
* if the current ActivityRecord is above it in the stack
* <p>
- * A transition is blocked ({@code false} returned) if all of the following are met:
+ * A transition is blocked if all of the following are met:
* <pre>
* 1. The source activity and the current activity record belong to different apps
* (i.e, have different UIDs).
- * 2. Both the source activity and the current activity target U+
- * 3. The current activity has not set
+ * 2. The current activity target V+
+ * 3. The current app has set
+ * {@link R.styleable#AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow}
+ * to {@code false}
+ * 4. The current activity has not set
* {@link ActivityRecord#setAllowCrossUidActivitySwitchFromBelow(boolean)} to {@code true}
* </pre>
*
- * Returns a class where the elements are:
- * <pre>
- * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
- * consideration feature flag and targetSdk).
- * wouldBlockActivityStartIgnoringFlags: {@code true} if we should warn about the transition via
- * toasts. This happens if the transition would be blocked in case both the app was targeting V+
- * and the feature was enabled.
- * </pre>
*
* @param sourceUid The source (s) activity performing the state change
*/
- private BlockActivityStart blockCrossUidActivitySwitchFromBelow(ActivityRecord ar,
- int sourceUid) {
+ private BlockActivityStart checkCrossUidActivitySwitchFromBelow(ActivityRecord ar,
+ int sourceUid, BlockActivityStart bas) {
if (ar.isUid(sourceUid)) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ return bas.matchesSource();
}
- if (!blockCrossUidActivitySwitchFromBelowForActivity(ar)) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ // We don't need to check package level if activity has opted out.
+ if (ar.mAllowCrossUidActivitySwitchFromBelow) {
+ bas.mTopActivityOptedIn = false;
+ return bas.matchesSource();
}
- // At this point, we would block if the feature is launched and both apps were V+
- // Since we have a feature flag, we need to check that too
- // TODO(b/258792202) Replace with CompatChanges and replace Pair with boolean once feature
- // flag is removed
- boolean restrictActivitySwitch =
- ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(ar.getUid())
- && ActivitySecurityModelFeatureFlags
- .shouldRestrictActivitySwitch(sourceUid);
- if (restrictActivitySwitch) {
- return BlockActivityStart.BLOCK;
- } else {
- return BlockActivityStart.LOG_ONLY;
+ if (!CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, ar.getUid())) {
+ return bas;
}
+
+ if (ar.isUid(SYSTEM_UID)) {
+ return bas.optedIn(ar);
+ }
+
+ String packageName = ar.packageName;
+ if (packageName == null) {
+ Slog.wtf(TAG, "Package name: " + ar + " not found.");
+ return bas.optedIn(ar);
+ }
+
+ PackageManager pm = mService.mContext.getPackageManager();
+ ApplicationInfo applicationInfo;
+
+ try {
+ applicationInfo = pm.getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf(TAG, "Package name: " + packageName + " not found.");
+ return bas.optedIn(ar);
+ }
+
+ return applicationInfo.allowCrossUidActivitySwitchFromBelow ? bas : bas.optedIn(ar);
}
/**
@@ -1502,8 +1509,9 @@ public class BackgroundActivityStartController {
@Nullable ActivityRecord sourceRecord, @NonNull ActivityRecord targetRecord,
@Nullable Task targetTask, @Nullable ActivityRecord targetTopActivity,
int realCallingUid, @BalCode int balCode,
- boolean blockActivityStartAndFeatureEnabled, boolean taskToFront,
- boolean avoidMoveTaskToFront) {
+ boolean enforceBlock, boolean taskToFront,
+ boolean avoidMoveTaskToFront, boolean allowedByGracePeriod,
+ ActivityRecord activityOptedIn) {
final String prefix = "[ASM] ";
Function<ActivityRecord, String> recordToString = (ar) -> {
if (ar == null) {
@@ -1519,9 +1527,16 @@ public class BackgroundActivityStartController {
StringJoiner joiner = new StringJoiner("\n");
joiner.add(prefix + "------ Activity Security " + action + " Debug Logging Start ------");
- joiner.add(prefix + "Block Enabled: " + blockActivityStartAndFeatureEnabled);
+ joiner.add(prefix + "Block Enabled: " + enforceBlock);
+ if (!enforceBlock) {
+ joiner.add(prefix + "Feature Flag Enabled: " + android.security
+ .Flags.asmRestrictionsEnabled());
+ joiner.add(prefix + "Mendel Override: " + ActivitySecurityModelFeatureFlags
+ .asmRestrictionsEnabledForAll());
+ }
joiner.add(prefix + "ASM Version: " + ActivitySecurityModelFeatureFlags.ASM_VERSION);
joiner.add(prefix + "System Time: " + SystemClock.uptimeMillis());
+ joiner.add(prefix + "Activity Opted In: " + recordToString.apply(activityOptedIn));
boolean targetTaskMatchesSourceTask = targetTask != null
&& sourceRecord != null && sourceRecord.getTask() == targetTask;
@@ -1561,6 +1576,7 @@ public class BackgroundActivityStartController {
joiner.add(prefix + "TaskToFront: " + taskToFront);
joiner.add(prefix + "AvoidMoveToFront: " + avoidMoveTaskToFront);
joiner.add(prefix + "BalCode: " + balCodeToString(balCode));
+ joiner.add(prefix + "Allowed By Grace Period: " + allowedByGracePeriod);
joiner.add(prefix + "LastResumedActivity: "
+ recordToString.apply(mService.mLastResumedActivity));
@@ -1588,6 +1604,44 @@ public class BackgroundActivityStartController {
return joiner.toString();
}
+ private boolean allowedByAsmGracePeriod(int callingUid, @Nullable ActivityRecord sourceRecord,
+ @Nullable Task targetTask, @BalCode int balCode, boolean taskToFront,
+ boolean avoidMoveTaskToFront) {
+ if (balCode == BAL_ALLOW_GRACE_PERIOD) {
+ // Allow if launching into new task, and caller matches most recently finished activity
+ if (taskToFront && mTopFinishedActivity != null
+ && mTopFinishedActivity.mUid == callingUid) {
+ return true;
+ }
+
+ // Launching into existing task - allow if matches most recently finished activity
+ // within the task.
+ // We can reach here multiple ways:
+ // 1. activity in fg fires intent (taskToFront = false, sourceRecord is available)
+ // 2. activity in bg fires intent (taskToFront = false, sourceRecord is available)
+ // 3. activity in bg fires intent with NEW_FLAG (taskToFront = true,
+ // avoidMoveTaskToFront = true, sourceRecord is available)
+ // 4. activity in bg fires PI (taskToFront = true, avoidMoveTaskToFront = true,
+ // sourceRecord is not available, targetTask may be available)
+ if (!taskToFront || avoidMoveTaskToFront) {
+ if (targetTask != null) {
+ FinishedActivityEntry finishedEntry =
+ mTaskIdToFinishedActivity.get(targetTask.mTaskId);
+ if (finishedEntry != null && finishedEntry.mUid == callingUid) {
+ return true;
+ }
+ }
+
+ if (sourceRecord != null) {
+ FinishedActivityEntry finishedEntry =
+ mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
+ return finishedEntry != null && finishedEntry.mUid == callingUid;
+ }
+ }
+ }
+ return false;
+ }
+
private static boolean isSystemExemptFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_WINDOW_MANAGER,
@@ -1698,55 +1752,22 @@ public class BackgroundActivityStartController {
}
}
- /**
- * Activity level allowCrossUidActivitySwitchFromBelow defaults to false.
- * Package level defaults to true.
- * We block the launch if dev has explicitly set package level to false, and activity level has
- * not opted out
- */
- private boolean blockCrossUidActivitySwitchFromBelowForActivity(@NonNull ActivityRecord ar) {
- // We don't need to check package level if activity has opted out.
- if (ar.mAllowCrossUidActivitySwitchFromBelow) {
- return false;
- }
-
- if (ActivitySecurityModelFeatureFlags.asmRestrictionsEnabledForAll()) {
- return true;
- }
-
- String packageName = ar.packageName;
- if (packageName == null) {
- return false;
- }
-
- PackageManager pm = mService.mContext.getPackageManager();
- ApplicationInfo applicationInfo;
-
- try {
- applicationInfo = pm.getApplicationInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.wtf(TAG, "Package name: " + packageName + " not found.");
- return false;
+ private static class BlockActivityStart {
+ private boolean mTopActivityOptedIn;
+ private boolean mTopActivityMatchesSource;
+ private ActivityRecord mActivityOptedIn;
+
+ BlockActivityStart optedIn(ActivityRecord activity) {
+ mTopActivityOptedIn = true;
+ if (mActivityOptedIn == null) {
+ mActivityOptedIn = activity;
+ }
+ return this;
}
- return !applicationInfo.allowCrossUidActivitySwitchFromBelow;
- }
-
- private static class BlockActivityStart {
- private static final BlockActivityStart ACTIVITY_START_ALLOWED =
- new BlockActivityStart(false, false);
- private static final BlockActivityStart LOG_ONLY = new BlockActivityStart(false, true);
- private static final BlockActivityStart BLOCK = new BlockActivityStart(true, true);
- // We should block if feature flag is enabled
- private final boolean mBlockActivityStartIfFlagEnabled;
- // Used for logging/toasts. Would we block if target sdk was V and feature was
- // enabled?
- private final boolean mWouldBlockActivityStartIgnoringFlag;
-
- private BlockActivityStart(boolean shouldBlockActivityStart,
- boolean wouldBlockActivityStartIgnoringFlags) {
- this.mBlockActivityStartIfFlagEnabled = shouldBlockActivityStart;
- this.mWouldBlockActivityStartIgnoringFlag = wouldBlockActivityStartIgnoringFlags;
+ BlockActivityStart matchesSource() {
+ mTopActivityMatchesSource = true;
+ return this;
}
}
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index e48e4e84d60d..816fe1dbae94 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -22,6 +22,7 @@ import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -179,6 +180,22 @@ class ClientLifecycleManager {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
+ /** Executes the pending transaction for the given client process. */
+ void dispatchPendingTransaction(@NonNull IApplicationThread client) {
+ if (!Flags.bundleClientTransactionFlag()) {
+ return;
+ }
+ final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
+ if (pendingTransaction != null) {
+ try {
+ scheduleTransaction(pendingTransaction);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to deliver pending transaction", e);
+ // TODO(b/323801078): apply cleanup for individual transaction item if needed.
+ }
+ }
+ }
+
/**
* Called to when {@link WindowSurfacePlacer#continueLayout}.
* Dispatches all pending transactions unless there is an ongoing/scheduled layout, in which
@@ -233,4 +250,17 @@ class ClientLifecycleManager {
&& !mWms.mWindowPlacerLocked.isTraversalScheduled()
&& !mWms.mWindowPlacerLocked.isInLayout();
}
+
+ /**
+ * Guards the bundleClientTransactionFlag feature with targetSDK on Android 15+.
+ *
+ * Suppressing because it can't guard with @EnabledSince on VANILLA_ICE_CREAM yet since the
+ * version is not published.
+ *
+ * TODO(b/324203798): update in V
+ */
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ static boolean shouldDispatchCompatClientTransactionIndependently(int appTargetSdk) {
+ return appTargetSdk <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 8717098ff8e8..a914c0712319 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -650,6 +650,14 @@ final class ContentRecorder implements WindowContainerListener {
if (isCurrentlyRecording() && mLastRecordedBounds != null) {
mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(
isVisibleRequested);
+
+ if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+ // If capturing a task, then the toggle visibility of the recorded surface to match
+ // visibility of the task, so we don't capture any mid-transition frames
+ mRecordedWindowContainer.getSyncTransaction()
+ .setVisibility(mRecordedSurface, isVisibleRequested);
+ mRecordedWindowContainer.scheduleAnimation();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index ea31e632cfb8..9fee3433f6be 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -215,11 +215,11 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
* when {@link android.inputmethodservice.InputMethodService} requests to show IME
* on {@param imeTarget}.
*
- * @param imeTarget imeTarget on which IME show request is coming from.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param imeTarget imeTarget on which IME request is coming from.
+ * @param statsToken the token tracking the current IME request.
*/
void scheduleShowImePostLayout(InsetsControlTarget imeTarget,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
mImeRequester = imeTarget;
// Cancel the pre-existing stats token, if any.
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index b74eb56ebdca..cc3de7a3462c 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -60,8 +60,8 @@ interface InsetsControlTarget {
* Instructs the control target to show inset sources.
*
* @param types to specify which types of insets source window should be shown.
- * @param fromIme {@code true} if IME show request originated from {@link InputMethodService}.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param fromIme {@code true} if the IME request originated from {@link InputMethodService}.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void showInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
@@ -71,8 +71,8 @@ interface InsetsControlTarget {
* Instructs the control target to hide inset sources.
*
* @param types to specify which types of insets source window should be hidden.
- * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}.
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param fromIme {@code true} if the IME request originated from {@link InputMethodService}.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void hideInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cd968067e289..83f44d23dbb1 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -311,7 +311,7 @@ class InsetsSourceProvider {
return mInsetsHint;
}
final WindowState win = mWindowContainer.asWindowState();
- if (win != null && win.mGivenInsetsPending && win.mAttrs.providedInsets == null) {
+ if (win != null && win.mGivenInsetsPending) {
return mInsetsHint;
}
if (mInsetsHintStale) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 908cbd340236..30134d815fa6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -336,19 +336,19 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public boolean performHapticFeedback(int effectId, boolean always) {
+ public boolean performHapticFeedback(int effectId, boolean always, boolean fromIme) {
final long ident = Binder.clearCallingIdentity();
try {
return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
- effectId, always, null);
+ effectId, always, null, fromIme);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
- public void performHapticFeedbackAsync(int effectId, boolean always) {
- performHapticFeedback(effectId, always);
+ public void performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme) {
+ performHapticFeedback(effectId, always, fromIme);
}
/* Drag/drop */
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1353ff09b292..e16d869fdcd9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3751,9 +3751,11 @@ class Task extends TaskFragment {
// Boost the adjacent TaskFragment for dimmer if needed.
final TaskFragment taskFragment = wc.asTaskFragment();
if (taskFragment != null && taskFragment.isEmbedded()) {
+ taskFragment.mDimmerSurfaceBoosted = false;
final TaskFragment adjacentTf = taskFragment.getAdjacentTaskFragment();
if (adjacentTf != null && adjacentTf.shouldBoostDimmer()) {
adjacentTf.assignLayer(t, layer++);
+ adjacentTf.mDimmerSurfaceBoosted = true;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 85d81c4db2ca..a818a721f964 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -216,6 +216,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
Dimmer mDimmer = Dimmer.DIMMER_REFACTOR
? new SmoothDimmer(this) : new LegacyDimmer(this);
+ /** {@code true} if the dimmer surface is boosted. {@code false} otherwise. */
+ boolean mDimmerSurfaceBoosted;
+
/** Apply the dim layer on the embedded TaskFragment. */
static final int EMBEDDED_DIM_AREA_TASK_FRAGMENT = 0;
@@ -1881,7 +1884,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(),
PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately, autoEnteringPip));
+ pauseImmediately, autoEnteringPip));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 594043d380c9..9b19a707d7be 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -349,7 +349,7 @@ class WallpaperController {
final Rect lastWallpaperBounds = wallpaperWin.getParentFrame();
int screenWidth = lastWallpaperBounds.width();
int screenHeight = lastWallpaperBounds.height();
- float screenRatio = ((float) screenWidth) / screenHeight;
+ float screenRatio = (float) screenWidth / screenHeight;
Point screenSize = new Point(screenWidth, screenHeight);
WallpaperWindowToken token = wallpaperWin.mToken.asWallpaperToken();
@@ -399,20 +399,32 @@ class WallpaperController {
Point bitmapSize = new Point(
wallpaperWin.mRequestedWidth, wallpaperWin.mRequestedHeight);
SparseArray<Rect> cropHints = token.getCropHints();
- wallpaperFrame = mWallpaperCropUtils.getCrop(
- screenSize, bitmapSize, cropHints, wallpaperWin.isRtl());
-
- cropZoom = wallpaperFrame.isEmpty() ? 1f
- : ((float) screenHeight) / wallpaperFrame.height() / wallpaperWin.mVScale;
-
- // A positive x / y offset shifts the wallpaper to the right / bottom respectively.
- cropOffsetX = -wallpaperFrame.left
- + (int) ((cropZoom - 1f) * wallpaperFrame.height() * screenRatio / 2f);
- cropOffsetY = -wallpaperFrame.top
- + (int) ((cropZoom - 1f) * wallpaperFrame.height() / 2f);
-
- diffWidth = (int) (wallpaperFrame.width() * wallpaperWin.mHScale) - screenWidth;
- diffHeight = (int) (wallpaperFrame.height() * wallpaperWin.mVScale) - screenHeight;
+ wallpaperFrame = bitmapSize.x <= 0 || bitmapSize.y <= 0 ? wallpaperWin.getFrame()
+ : mWallpaperCropUtils.getCrop(screenSize, bitmapSize, cropHints,
+ wallpaperWin.isRtl());
+ int frameWidth = wallpaperFrame.width();
+ int frameHeight = wallpaperFrame.height();
+ float frameRatio = (float) frameWidth / frameHeight;
+
+ // If the crop is proportionally wider/taller than the screen, scale it so that its
+ // height/width matches the screen height/width, and use the additional width/height
+ // for parallax (respectively).
+ boolean scaleHeight = frameRatio >= screenRatio;
+ cropZoom = wallpaperFrame.isEmpty() ? 1f : scaleHeight
+ ? (float) screenHeight / frameHeight / wallpaperWin.mVScale
+ : (float) screenWidth / frameWidth / wallpaperWin.mHScale;
+
+ // The dimensions of the frame, without the additional width or height for parallax.
+ float w = scaleHeight ? frameHeight * screenRatio : frameWidth;
+ float h = scaleHeight ? frameHeight : frameWidth / screenRatio;
+
+ // Note: a positive x/y offset shifts the wallpaper to the right/bottom respectively.
+ cropOffsetX = -wallpaperFrame.left + (int) ((cropZoom - 1f) * w / 2f);
+ cropOffsetY = -wallpaperFrame.top + (int) ((cropZoom - 1f) * h / 2f);
+
+ // Available width or height for parallax
+ diffWidth = (int) ((frameWidth - w) * wallpaperWin.mHScale);
+ diffHeight = (int) ((frameHeight - h) * wallpaperWin.mVScale);
} else {
wallpaperFrame = wallpaperWin.getFrame();
cropZoom = 1f;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 5df2edc808f6..77319cc0ba8a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -829,20 +829,20 @@ public abstract class WindowManagerInternal {
* Show IME on imeTargetWindow once IME has finished layout.
*
* @param imeTargetWindowToken token of the (IME target) window which IME should be shown.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*/
public abstract void showImePostLayout(IBinder imeTargetWindowToken,
- @Nullable ImeTracker.Token statsToken);
+ @NonNull ImeTracker.Token statsToken);
/**
* Hide IME using imeTargetWindow when requested.
*
- * @param imeTargetWindowToken token of the (IME target) window on which requests hiding IME.
+ * @param imeTargetWindowToken token of the (IME target) window which requests hiding IME.
* @param displayId the id of the display the IME is on.
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*/
public abstract void hideIme(IBinder imeTargetWindowToken, int displayId,
- @Nullable ImeTracker.Token statsToken);
+ @NonNull ImeTracker.Token statsToken);
/**
* Tell window manager about a package that should be running with a restricted range of
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c93cc074aa3d..a055db274ae8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2109,7 +2109,15 @@ public class WindowManagerService extends IWindowManager.Stub
+ ", touchableRegion=" + w.mGivenTouchableRegion + " -> " + touchableRegion
+ ", touchableInsets " + w.mTouchableInsets + " -> " + touchableInsets);
if (w != null) {
+ final boolean wasGivenInsetsPending = w.mGivenInsetsPending;
w.mGivenInsetsPending = false;
+ if ((!wasGivenInsetsPending || !w.hasInsetsSourceProvider())
+ && w.mTouchableInsets == touchableInsets
+ && w.mGivenContentInsets.equals(contentInsets)
+ && w.mGivenVisibleInsets.equals(visibleInsets)
+ && w.mGivenTouchableRegion.equals(touchableRegion)) {
+ return;
+ }
w.mGivenContentInsets.set(contentInsets);
w.mGivenVisibleInsets.set(visibleInsets);
w.mGivenTouchableRegion.set(touchableRegion);
@@ -6706,7 +6714,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void dumpLogStatus(PrintWriter pw) {
pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)");
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("Deprecated legacy command. Use Perfetto commands instead.");
return;
}
@@ -8258,12 +8266,17 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void showImePostLayout(IBinder imeTargetWindowToken,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
synchronized (mGlobalLock) {
InputTarget imeTarget = getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
if (imeTarget == null) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
+
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
imeTarget = controlTarget.getWindow();
@@ -8278,7 +8291,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void hideIme(IBinder imeTargetWindowToken, int displayId,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.hideIme");
synchronized (mGlobalLock) {
WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
@@ -9209,6 +9222,11 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
+ if (taskFragment.mDimmerSurfaceBoosted) {
+ // Skip if the TaskFragment currently has dimmer surface boosted.
+ return false;
+ }
+
final ActivityRecord topActivity =
taskFragment.getTask().topRunningActivity(true /* focusableOnly */);
if (topActivity == null || topActivity == focusedWindow.mActivityRecord) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d6fc01aeadd2..a7eb444ecb59 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -329,15 +329,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
deferred);
wctApplied.meet();
if (needsSetReady) {
- // TODO(b/294925498): Remove this once we have accurate ready
- // tracking.
- if (hasActivityLaunch(wct) && !mService.mRootWindowContainer
- .allPausedActivitiesComplete()) {
- // WCT is launching an activity, so we need to wait for its
- // lifecycle events.
- return;
- }
- nextTransition.setAllReady();
+ setAllReadyIfNeeded(nextTransition, wct);
}
});
return nextTransition.getToken();
@@ -390,7 +382,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
- private static boolean hasActivityLaunch(WindowContainerTransaction wct) {
+ private static boolean hasActivityLaunch(@NonNull WindowContainerTransaction wct) {
for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
if (wct.getHierarchyOps().get(i).getType() == HIERARCHY_OP_TYPE_LAUNCH_TASK) {
return true;
@@ -399,6 +391,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return false;
}
+ private boolean isCreatedTaskFragmentReady(@NonNull WindowContainerTransaction wct) {
+ for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
+ if (op.getType() != HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION
+ || op.getTaskFragmentOperation().getOpType()
+ != OP_TYPE_CREATE_TASK_FRAGMENT) {
+ continue;
+ }
+ final IBinder tfToken = op.getTaskFragmentOperation()
+ .getTaskFragmentCreationParams().getFragmentToken();
+ final TaskFragment taskFragment = getTaskFragment(tfToken);
+ if (taskFragment != null && !taskFragment.isReadyToTransit()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void setAllReadyIfNeeded(@NonNull Transition transition,
+ @NonNull WindowContainerTransaction wct) {
+ // TODO(b/294925498): Remove this once we have accurate ready tracking.
+ if (hasActivityLaunch(wct) && !mService.mRootWindowContainer
+ .allPausedActivitiesComplete()) {
+ // WCT is launching an activity, so we need to wait for its
+ // lifecycle events.
+ return;
+ }
+ if (!isCreatedTaskFragmentReady(wct)) {
+ // When the organizer intercepts a #startActivity, it will create an empty TaskFragment
+ // for that specific incoming starting activity. We don't want to set all ready here,
+ // because we requires that #startActivity to be included in this transition, and NOT be
+ // in its own transition.
+ // TODO(b/232042367): explicitly ensure the #startActivity and this transaction are in
+ // the same transition instead of relying on this possible racing condition.
+ return;
+ }
+
+ transition.setAllReady();
+ }
+
@Override
public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
@NonNull IWindowContainerTransactionCallback callback,
@@ -529,7 +561,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
mTransitionController.requestStartTransition(transition, null /* startTask */,
remoteTransition, null /* displayChange */);
- transition.setAllReady();
+ setAllReadyIfNeeded(transition, wct);
};
mTransitionController.startCollectOrQueue(transition, doApply);
} finally {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a7a28c282ff9..1106a95fcdce 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1342,7 +1342,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// This window doesn't provide any insets.
return;
}
- if (mGivenInsetsPending && mAttrs.providedInsets == null) {
+ if (mGivenInsetsPending) {
// The given insets are pending, and they are not reliable for now. The source frame
// should be updated after the new given insets are sent to window manager.
return;
@@ -2041,6 +2041,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (!isVisible()) {
return;
}
+ final WallpaperWindowToken wallpaperToken = mToken.asWallpaperToken();
+ if (wallpaperToken != null) {
+ if (wallpaperToken.hasVisibleNotDrawnWallpaper()) {
+ outWaitingForDrawn.add(this);
+ }
+ return;
+ }
if (mActivityRecord != null) {
if (!mActivityRecord.isVisibleRequested()) return;
if (mActivityRecord.allDrawn) {
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 424d50434010..b0e71bda787a 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -109,6 +109,9 @@ class WindowTracing {
return;
}
synchronized (mEnabledLock) {
+ if (!android.tracing.Flags.perfettoProtologTracing()) {
+ ((LegacyProtoLogImpl) ProtoLog.getSingleInstance()).startProtoLog(pw);
+ }
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer();
mEnabled = mEnabledLockFree = true;
@@ -136,6 +139,9 @@ class WindowTracing {
writeTraceToFileLocked();
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
}
+ if (!android.tracing.Flags.perfettoProtologTracing()) {
+ ((LegacyProtoLogImpl) ProtoLog.getSingleInstance()).stopProtoLog(pw, true);
+ }
}
/**
@@ -155,13 +161,13 @@ class WindowTracing {
logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
writeTraceToFileLocked();
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
- if (!android.tracing.Flags.perfettoProtolog()) {
+ if (!android.tracing.Flags.perfettoProtologTracing()) {
((LegacyProtoLogImpl) mProtoLog).stopProtoLog(pw, true);
}
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer();
mEnabled = mEnabledLockFree = true;
- if (!android.tracing.Flags.perfettoProtolog()) {
+ if (!android.tracing.Flags.perfettoProtologTracing()) {
((LegacyProtoLogImpl) mProtoLog).startProtoLog(pw);
}
}
diff --git a/services/core/jni/com_android_server_accessibility_BrailleDisplayConnection.cpp b/services/core/jni/com_android_server_accessibility_BrailleDisplayConnection.cpp
index 9a509a71cf92..c3375236098a 100644
--- a/services/core/jni/com_android_server_accessibility_BrailleDisplayConnection.cpp
+++ b/services/core/jni/com_android_server_accessibility_BrailleDisplayConnection.cpp
@@ -40,7 +40,7 @@ constexpr int UNIQ_SIZE_MAX = 64;
} // anonymous namespace
static jint com_android_server_accessibility_BrailleDisplayConnection_getHidrawDescSize(
- JNIEnv* env, jobject thiz, int fd) {
+ JNIEnv* env, jclass /*clazz*/, int fd) {
int size = 0;
if (ioctl(fd, HIDIOCGRDESCSIZE, &size) < 0) {
return -1;
@@ -49,7 +49,7 @@ static jint com_android_server_accessibility_BrailleDisplayConnection_getHidrawD
}
static jbyteArray com_android_server_accessibility_BrailleDisplayConnection_getHidrawDesc(
- JNIEnv* env, jobject thiz, int fd, int descSize) {
+ JNIEnv* env, jclass /*clazz*/, int fd, int descSize) {
struct hidraw_report_descriptor desc;
desc.size = descSize;
if (ioctl(fd, HIDIOCGRDESC, &desc) < 0) {
@@ -63,9 +63,8 @@ static jbyteArray com_android_server_accessibility_BrailleDisplayConnection_getH
return result;
}
-static jstring com_android_server_accessibility_BrailleDisplayConnection_getHidrawUniq(JNIEnv* env,
- jobject thiz,
- int fd) {
+static jstring com_android_server_accessibility_BrailleDisplayConnection_getHidrawUniq(
+ JNIEnv* env, jclass /*clazz*/, int fd) {
char buf[UNIQ_SIZE_MAX];
if (ioctl(fd, HIDIOCGRAWUNIQ(UNIQ_SIZE_MAX), buf) < 0) {
return nullptr;
@@ -74,9 +73,8 @@ static jstring com_android_server_accessibility_BrailleDisplayConnection_getHidr
return env->NewStringUTF(buf);
}
-static jint com_android_server_accessibility_BrailleDisplayConnection_getHidrawBusType(JNIEnv* env,
- jobject thiz,
- int fd) {
+static jint com_android_server_accessibility_BrailleDisplayConnection_getHidrawBusType(
+ JNIEnv* env, jclass /*clazz*/, int fd) {
struct hidraw_devinfo info;
if (ioctl(fd, HIDIOCGRAWINFO, &info) < 0) {
return -1;
diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp
index ccd9bd0a50ca..b2bdaa35f28c 100644
--- a/services/core/jni/com_android_server_hint_HintManagerService.cpp
+++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp
@@ -24,16 +24,17 @@
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHintSessionWrapper.h>
#include <utils/Log.h>
#include <unordered_map>
#include "jni.h"
-using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::SessionMode;
using aidl::android::hardware::power::WorkDuration;
+using android::power::PowerHintSessionWrapper;
using android::base::StringPrintf;
@@ -49,7 +50,7 @@ static struct {
} gWorkDurationInfo;
static power::PowerHalController gPowerHalController;
-static std::unordered_map<jlong, std::shared_ptr<IPowerHintSession>> gSessionMap;
+static std::unordered_map<jlong, std::shared_ptr<PowerHintSessionWrapper>> gSessionMap;
static std::mutex gSessionMapLock;
static int64_t getHintSessionPreferredRate() {
@@ -76,45 +77,45 @@ static jlong createHintSession(JNIEnv* env, int32_t tgid, int32_t uid,
}
static void pauseHintSession(JNIEnv* env, int64_t session_ptr) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->pause();
}
static void resumeHintSession(JNIEnv* env, int64_t session_ptr) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->resume();
}
static void closeHintSession(JNIEnv* env, int64_t session_ptr) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->close();
std::unique_lock<std::mutex> sessionLock(gSessionMapLock);
gSessionMap.erase(session_ptr);
}
static void updateTargetWorkDuration(int64_t session_ptr, int64_t targetDurationNanos) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->updateTargetWorkDuration(targetDurationNanos);
}
static void reportActualWorkDuration(int64_t session_ptr,
const std::vector<WorkDuration>& actualDurations) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->reportActualWorkDuration(actualDurations);
}
static void sendHint(int64_t session_ptr, SessionHint hint) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->sendHint(hint);
}
static void setThreads(int64_t session_ptr, const std::vector<int32_t>& threadIds) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->setThreads(threadIds);
}
static void setMode(int64_t session_ptr, SessionMode mode, bool enabled) {
- auto appSession = reinterpret_cast<IPowerHintSession*>(session_ptr);
+ auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->setMode(mode, enabled);
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 027337f04b6e..f5e1e41dbae4 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -113,8 +113,8 @@ public class CredentialManagerUi {
/** Creates intent that is ot be invoked to cancel an in-progress UI session. */
public Intent createCancelIntent(IBinder requestId, String packageName) {
- return IntentFactory.createCancelUiIntent(requestId, /*shouldShowCancellationUi=*/ true,
- packageName);
+ return IntentFactory.createCancelUiIntent(mContext, requestId,
+ /*shouldShowCancellationUi=*/ true, packageName);
}
/**
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 3cb98eb4cd7a..eff53de75ff4 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -41,6 +41,8 @@ import android.service.credentials.CredentialProviderService;
import android.service.credentials.PermissionUtils;
import android.util.Slog;
+import com.android.server.credentials.metrics.ApiStatus;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -180,7 +182,7 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
} else {
Slog.w(TAG, "onUiCancellation called but finalResponseReceiver not found");
}
- finishSession(/*propagateCancellation=*/false);
+ finishSession(/*propagateCancellation=*/false, ApiStatus.FAILURE.getMetricCode());
}
@Override
@@ -221,9 +223,10 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
resultData.putParcelable(
CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response);
mFinalResponseReceiver.send(Constants.SUCCESS_CREDMAN_SELECTOR, resultData);
- finishSession(/*propagateCancellation=*/ false);
+ finishSession(/*propagateCancellation=*/ false, ApiStatus.SUCCESS.getMetricCode());
} else {
Slog.w(TAG, "onFinalResponseReceived result receiver not found for pinned entry");
+ finishSession(/*propagateCancellation=*/ false, ApiStatus.FAILURE.getMetricCode());
}
}
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 633c9c4cb8e1..a5b9aa68b22e 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -175,7 +175,7 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
() -> {
Slog.d(TAG, "Cancellation invoked from the client - clearing session");
boolean isUiActive = maybeCancelUi();
- finishSession(!isUiActive);
+ finishSession(!isUiActive, ApiStatus.CLIENT_CANCELED.getMetricCode());
}
);
}
@@ -231,7 +231,8 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
return;
}
if (isSessionCancelled()) {
- finishSession(/*propagateCancellation=*/true);
+ finishSession(/*propagateCancellation=*/true,
+ ApiStatus.CLIENT_CANCELED.getMetricCode());
return;
}
String providerId = selection.getProviderId();
@@ -257,11 +258,12 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
}
}
- protected void finishSession(boolean propagateCancellation) {
+ protected void finishSession(boolean propagateCancellation, int apiStatus) {
Slog.i(TAG, "finishing session with propagateCancellation " + propagateCancellation);
if (propagateCancellation) {
mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession);
}
+ mRequestSessionMetric.logApiCalledAtFinish(apiStatus);
mRequestSessionStatus = RequestSessionStatus.COMPLETE;
mProviders.clear();
clearRequestSessionLocked();
@@ -326,7 +328,8 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
mRequestSessionMetric.logCandidatePhaseMetrics(mProviders);
if (isSessionCancelled()) {
- finishSession(/*propagateCancellation=*/true);
+ finishSession(/*propagateCancellation=*/true,
+ ApiStatus.CLIENT_CANCELED.getMetricCode());
return providerDataList;
}
@@ -353,23 +356,20 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
return;
}
if (isSessionCancelled()) {
- mRequestSessionMetric.logApiCalledAtFinish(
- /*apiStatus=*/ ApiStatus.CLIENT_CANCELED.getMetricCode());
- finishSession(/*propagateCancellation=*/true);
+ finishSession(/*propagateCancellation=*/true,
+ ApiStatus.CLIENT_CANCELED.getMetricCode());
return;
}
try {
invokeClientCallbackSuccess(response);
- mRequestSessionMetric.logApiCalledAtFinish(
- /*apiStatus=*/ ApiStatus.SUCCESS.getMetricCode());
+ finishSession(/*propagateCancellation=*/false,
+ ApiStatus.SUCCESS.getMetricCode());
} catch (RemoteException e) {
mRequestSessionMetric.collectFinalPhaseProviderMetricStatus(
/*has_exception=*/ true, ProviderStatusForMetrics.FINAL_FAILURE);
Slog.e(TAG, "Issue while responding to client with a response : " + e);
- mRequestSessionMetric.logApiCalledAtFinish(
- /*apiStatus=*/ ApiStatus.FAILURE.getMetricCode());
+ finishSession(/*propagateCancellation=*/false, ApiStatus.FAILURE.getMetricCode());
}
- finishSession(/*propagateCancellation=*/false);
}
/**
@@ -387,9 +387,7 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
return;
}
if (isSessionCancelled()) {
- mRequestSessionMetric.logApiCalledAtFinish(
- /*apiStatus=*/ ApiStatus.CLIENT_CANCELED.getMetricCode());
- finishSession(/*propagateCancellation=*/true);
+ finishSession(/*propagateCancellation=*/true, ApiStatus.CLIENT_CANCELED.getMetricCode());
return;
}
@@ -399,8 +397,14 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
Slog.e(TAG, "Issue while responding to client with error : " + e);
}
boolean isUserCanceled = errorType.contains(MetricUtilities.USER_CANCELED_SUBSTRING);
- mRequestSessionMetric.logFailureOrUserCancel(isUserCanceled);
- finishSession(/*propagateCancellation=*/false);
+ if (isUserCanceled) {
+ mRequestSessionMetric.setHasExceptionFinalPhase(/* has_exception */ false);
+ finishSession(/*propagateCancellation=*/false,
+ ApiStatus.USER_CANCELED.getMetricCode());
+ } else {
+ finishSession(/*propagateCancellation=*/false,
+ ApiStatus.FAILURE.getMetricCode());
+ }
}
/**
@@ -419,7 +423,7 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
@Override
public void binderDied() {
Slog.d(TAG, "Client binder died - clearing session");
- finishSession(isUiWaitingForData());
+ finishSession(isUiWaitingForData(), ApiStatus.CLIENT_CANCELED.getMetricCode());
}
}
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
index 8adcfbcb974c..a77bd3e280dd 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
@@ -247,7 +247,7 @@ public class RequestSessionMetric {
*
* @param exceptionBitFinalPhase represents if the final phase provider had an exception
*/
- private void setHasExceptionFinalPhase(boolean exceptionBitFinalPhase) {
+ public void setHasExceptionFinalPhase(boolean exceptionBitFinalPhase) {
try {
mChosenProviderFinalPhaseMetric.setHasException(exceptionBitFinalPhase);
} catch (Exception e) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 12f44074a4ad..6aeb4fd53905 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -225,7 +225,7 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
policyDefinition, userId)) {
return;
@@ -350,7 +350,7 @@ final class DevicePolicyEngine {
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
}
@@ -496,7 +496,7 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
policyDefinition, UserHandle.USER_ALL)) {
return;
@@ -568,7 +568,7 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
decreasePolicySizeForAdmin(policyState, enforcingAdmin);
}
@@ -1892,7 +1892,7 @@ final class DevicePolicyEngine {
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
if (mAdminPolicySize != null) {
for (int i = 0; i < mAdminPolicySize.size(); i++) {
int userId = mAdminPolicySize.keyAt(i);
@@ -1916,7 +1916,7 @@ final class DevicePolicyEngine {
private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return;
}
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
@@ -2081,7 +2081,7 @@ final class DevicePolicyEngine {
private void readMaxPolicySizeInner(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return;
}
mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0f97f4a7cdc0..c37946b4d750 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -27,6 +27,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECU
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUTOFILL;
@@ -83,7 +84,6 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER;
-import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_VPN;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WALLPAPER;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
@@ -196,11 +196,11 @@ import static android.app.admin.DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PR
import static android.app.admin.DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_HAS_DEVICE_OWNER;
import static android.app.admin.DevicePolicyManager.STATUS_HAS_PAIRED;
+import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_ONLY_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS;
import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_ONLY_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.STATUS_OK;
import static android.app.admin.DevicePolicyManager.STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
import static android.app.admin.DevicePolicyManager.STATUS_SYSTEM_USER;
@@ -12062,7 +12062,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (packageList != null) {
- if (!Flags.devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String pkg : packageList) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -13771,7 +13771,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- if (!Flags.devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
}
@@ -14385,7 +14385,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- if (!Flags.devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String pkg : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -23389,7 +23389,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
}
- private boolean isUnicornFlagEnabled() {
+ static boolean isUnicornFlagEnabled() {
return false;
}
@@ -24235,7 +24235,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
@@ -24247,7 +24247,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public int getMaxPolicyStorageLimit(String callerPackageName) {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return -1;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index c108deaf33bc..a7adc5b6d925 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -66,6 +66,10 @@ final class PolicyEnforcerCallbacks {
private static final String LOG_TAG = "PolicyEnforcerCallbacks";
static boolean setAutoTimezoneEnabled(@Nullable Boolean enabled, @NonNull Context context) {
+ if (!DevicePolicyManagerService.isUnicornFlagEnabled()) {
+ Slogf.w(LOG_TAG, "Trying to enforce setAutoTimezoneEnabled while flag is off.");
+ return true;
+ }
return Binder.withCleanCallingIdentity(() -> {
Objects.requireNonNull(context);
@@ -79,6 +83,10 @@ final class PolicyEnforcerCallbacks {
static boolean setPermissionGrantState(
@Nullable Integer grantState, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
+ if (!DevicePolicyManagerService.isUnicornFlagEnabled()) {
+ Slogf.w(LOG_TAG, "Trying to enforce setPermissionGrantState while flag is off.");
+ return true;
+ }
return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
if (!(policyKey instanceof PackagePermissionPolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
diff --git a/services/fakes/java/com/android/server/example/BlueManagerService.java b/services/fakes/java/com/android/server/example/BlueManagerService.java
new file mode 100644
index 000000000000..8e3c8d73df9c
--- /dev/null
+++ b/services/fakes/java/com/android/server/example/BlueManagerService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.example;
+
+import android.content.Context;
+import android.os.Binder;
+import android.ravenwood.example.BlueManager;
+
+import com.android.server.SystemService;
+
+public class BlueManagerService extends Binder {
+ public static class Lifecycle extends SystemService {
+ private BlueManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new BlueManagerService();
+ publishBinderService(BlueManager.SERVICE_NAME, mService);
+ }
+ }
+
+ @Override
+ public String getInterfaceDescriptor() {
+ return "blue";
+ }
+}
diff --git a/services/fakes/java/com/android/server/example/RedManagerService.java b/services/fakes/java/com/android/server/example/RedManagerService.java
new file mode 100644
index 000000000000..e0be7337698d
--- /dev/null
+++ b/services/fakes/java/com/android/server/example/RedManagerService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.example;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.ravenwood.example.BlueManager;
+import android.ravenwood.example.RedManager;
+
+import com.android.server.SystemService;
+
+import java.util.List;
+
+public class RedManagerService extends Binder {
+ private IBinder mBlueService;
+
+ public static class Lifecycle extends SystemService {
+ private RedManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context, List.of(BlueManager.class));
+ }
+
+ @Override
+ public void onStart() {
+ mService = new RedManagerService();
+ publishBinderService(RedManager.SERVICE_NAME, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ mService.mBlueService = getBinderService(BlueManager.SERVICE_NAME);
+ }
+ }
+ }
+
+ @Override
+ public String getInterfaceDescriptor() {
+ try {
+ // Obtain the answer from dependency, but then augment it to prove that the answer
+ // was channeled through us
+ return mBlueService.getInterfaceDescriptor() + "+red";
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
index 82d5247ebed8..209107e50902 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
@@ -37,6 +37,7 @@ import android.os.Handler;
import android.util.ArraySet;
import android.util.Dumpable;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.Surface;
import com.android.server.policy.BookStylePreferredScreenCalculator.PreferredScreen;
@@ -65,6 +66,7 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
private final Handler mHandler = new Handler();
private final PostureEstimator mPostureEstimator;
private final DisplayManager mDisplayManager;
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
/**
* Creates {@link BookStyleClosedStatePredicate}. It is expected that the device has a pair
@@ -140,10 +142,11 @@ public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceSt
public void onDisplayChanged(int displayId) {
if (displayId == DEFAULT_DISPLAY) {
final Display display = mDisplayManager.getDisplay(displayId);
+ display.getDisplayInfo(mDefaultDisplayInfo);
int displayState = display.getState();
boolean isDisplayOn = displayState == Display.STATE_ON;
mPostureEstimator.onDisplayPowerStatusChanged(isDisplayOn);
- mPostureEstimator.onDisplayRotationChanged(display.getRotation());
+ mPostureEstimator.onDisplayRotationChanged(mDefaultDisplayInfo.rotation);
}
}
diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
index 8d01b7a9c523..901f24dd9b0b 100644
--- a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
+++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
@@ -48,6 +48,7 @@ import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.Surface;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -629,7 +630,11 @@ public final class BookStyleDeviceStatePolicyTest {
}
private void sendScreenRotation(int rotation) {
- when(mDisplay.getRotation()).thenReturn(rotation);
+ doAnswer(invocation -> {
+ final DisplayInfo displayInfo = invocation.getArgument(0);
+ displayInfo.rotation = rotation;
+ return null;
+ }).when(mDisplay).getDisplayInfo(any());
mDisplayListenerCaptor.getAllValues().forEach((l) -> l.onDisplayChanged(DEFAULT_DISPLAY));
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e19f08cb04a1..9d95c5b4f649 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2774,9 +2774,12 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
// OnDevicePersonalizationSystemService
- t.traceBegin("StartOnDevicePersonalizationSystemService");
- mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS);
- t.traceEnd();
+ if (!com.android.server.flags.Flags.enableOdpFeatureGuard()
+ || SystemProperties.getBoolean("ro.system_settings.service.odp_enabled", true)) {
+ t.traceBegin("StartOnDevicePersonalizationSystemService");
+ mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS);
+ t.traceEnd();
+ }
// Profiling
if (android.server.Flags.telemetryApisService()) {
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index fc2eb2652d2d..c0d988d0c46b 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -20,27 +20,63 @@ import android.app.AppOpsManager
import android.companion.virtual.VirtualDeviceManager
import android.os.Handler
import android.os.UserHandle
+import android.permission.flags.Flags
import android.util.ArrayMap
import android.util.ArraySet
+import android.util.LongSparseArray
+import android.util.Slog
+import android.util.SparseArray
import android.util.SparseBooleanArray
import android.util.SparseIntArray
import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.util.IntPair
import com.android.server.appop.AppOpsCheckingServiceInterface
import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.AppOpUri
+import com.android.server.permission.access.DevicePermissionUri
+import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.PackageUri
+import com.android.server.permission.access.PermissionUri
import com.android.server.permission.access.UidUri
+import com.android.server.permission.access.appop.AppOpModes.MODE_ALLOWED
+import com.android.server.permission.access.appop.AppOpModes.MODE_FOREGROUND
+import com.android.server.permission.access.appop.AppOpModes.MODE_IGNORED
import com.android.server.permission.access.collection.forEachIndexed
import com.android.server.permission.access.collection.set
+import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.DevicePermissionPolicy
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.permission.access.permission.PermissionService
class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface {
private val packagePolicy =
service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy
private val appIdPolicy =
service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy
+ private val permissionPolicy =
+ service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
+ private val devicePermissionPolicy =
+ service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy
private val context = service.context
+
+ // Maps appop code to its runtime permission
+ private val runtimeAppOpToPermissionNames = SparseArray<String>()
+
+ // Maps runtime permission to its appop codes
+ private val runtimePermissionNameToAppOp = ArrayMap<String, Int>()
+
+ private var foregroundableOps = SparseBooleanArray()
+
+ /* Maps foreground permissions to their background permission. Background permissions aren't
+ required to be runtime */
+ private val foregroundToBackgroundPermissionName = ArrayMap<String, String>()
+
+ /* Maps background permissions to their foreground permissions. Background permissions aren't
+ required to be runtime */
+ private val backgroundToForegroundPermissionNames = ArrayMap<String, ArraySet<String>>()
+
private lateinit var handler: Handler
@Volatile private var listeners = ArraySet<AppOpsModeChangedListener>()
@@ -69,11 +105,60 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
}
override fun systemReady() {
- // Not implemented because upgrades are handled automatically.
+ if (Flags.runtimePermissionAppopsMappingEnabled()) {
+ createPermissionAppOpMapping()
+ val permissionListener = OnPermissionFlagsChangedListener()
+ permissionPolicy.addOnPermissionFlagsChangedListener(permissionListener)
+ devicePermissionPolicy.addOnPermissionFlagsChangedListener(permissionListener)
+ }
+ }
+
+ private fun createPermissionAppOpMapping() {
+ val permissions = service.getState { with(permissionPolicy) { getPermissions() } }
+
+ for (appOpCode in 0 until AppOpsManager._NUM_OP) {
+ AppOpsManager.opToPermission(appOpCode)?.let { permissionName ->
+ // Multiple ops might map to a single permission but only one is considered the
+ // runtime appop calculations.
+ if (appOpCode == AppOpsManager.permissionToOpCode(permissionName)) {
+ val permission = permissions[permissionName]!!
+ if (permission.isRuntime) {
+ runtimePermissionNameToAppOp[permissionName] = appOpCode
+ runtimeAppOpToPermissionNames[appOpCode] = permissionName
+ permission.permissionInfo.backgroundPermission?.let {
+ backgroundPermissionName ->
+ // Note: background permission may not be runtime,
+ // e.g. microphone/camera.
+ foregroundableOps[appOpCode] = true
+ foregroundToBackgroundPermissionName[permissionName] =
+ backgroundPermissionName
+ backgroundToForegroundPermissionNames
+ .getOrPut(backgroundPermissionName, ::ArraySet)
+ .add(permissionName)
+ }
+ }
+ }
+ }
+ }
}
override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray {
- return opNameMapToOpSparseArray(getUidModes(uid))
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ service.getState {
+ val modes =
+ with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) }
+ if (Flags.runtimePermissionAppopsMappingEnabled()) {
+ runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode ->
+ val mode = getUidModeFromPermissionState(appId, userId, permissionName)
+ if (mode != AppOpsManager.opToDefaultMode(appOpCode)) {
+ modes[appOpCode] = mode
+ }
+ }
+ }
+
+ return modes
+ }
}
override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray {
@@ -84,7 +169,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
val opName = AppOpsManager.opToPublicName(op)
- return service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } }
+ val permissionName = runtimeAppOpToPermissionNames[op]
+
+ return if (!Flags.runtimePermissionAppopsMappingEnabled() || permissionName == null) {
+ service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } }
+ } else {
+ service.getState { getUidModeFromPermissionState(appId, userId, permissionName) }
+ }
}
private fun getUidModes(uid: Int): ArrayMap<String, Int>? {
@@ -93,13 +184,66 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map
}
- override fun setUidMode(uid: Int, persistentDeviceId: String, op: Int, mode: Int): Boolean {
+ private fun GetStateScope.getUidModeFromPermissionState(
+ appId: Int,
+ userId: Int,
+ permissionName: String
+ ): Int {
+ val permissionFlags =
+ with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) }
+ val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName]
+ val backgroundPermissionFlags =
+ if (backgroundPermissionName != null) {
+ with(permissionPolicy) {
+ getPermissionFlags(appId, userId, backgroundPermissionName)
+ }
+ } else {
+ PermissionFlags.RUNTIME_GRANTED
+ }
+ val result = evaluateModeFromPermissionFlags(permissionFlags, backgroundPermissionFlags)
+ if (result != MODE_IGNORED) {
+ return result
+ }
+
+ val fullerPermissionName =
+ PermissionService.getFullerPermission(permissionName) ?: return result
+ return getUidModeFromPermissionState(appId, userId, fullerPermissionName)
+ }
+
+ private fun evaluateModeFromPermissionFlags(
+ foregroundFlags: Int,
+ backgroundFlags: Int = PermissionFlags.RUNTIME_GRANTED
+ ): Int =
+ if (PermissionFlags.isAppOpGranted(foregroundFlags)) {
+ if (PermissionFlags.isAppOpGranted(backgroundFlags)) {
+ MODE_ALLOWED
+ } else {
+ MODE_FOREGROUND
+ }
+ } else {
+ MODE_IGNORED
+ }
+
+ override fun setUidMode(uid: Int, persistentDeviceId: String, code: Int, mode: Int): Boolean {
+ if (
+ Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames
+ ) {
+ Slog.w(
+ LOG_TAG,
+ "Cannot set UID mode for runtime permission app op, uid = $uid," +
+ " code = ${AppOpsManager.opToName(code)}," +
+ " mode = ${AppOpsManager.modeToName(mode)}",
+ RuntimeException()
+ )
+ return false
+ }
+
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
- val opName = AppOpsManager.opToPublicName(op)
- var wasChanged = false
+ val appOpName = AppOpsManager.opToPublicName(code)
+ var wasChanged: Boolean
service.mutateState {
- wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) }
+ wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) }
}
return wasChanged
}
@@ -114,10 +258,23 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? =
service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map
- override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) {
- val opName = AppOpsManager.opToPublicName(op)
+ override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) {
+ val appOpName = AppOpsManager.opToPublicName(appOpCode)
+
+ if (
+ Flags.runtimePermissionAppopsMappingEnabled() &&
+ appOpCode in runtimeAppOpToPermissionNames
+ ) {
+ Slog.w(
+ LOG_TAG,
+ "(packageName=$packageName, userId=$userId)'s appop state" +
+ " for runtime op $appOpName should not be set directly.",
+ RuntimeException()
+ )
+ return
+ }
service.mutateState {
- with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) }
+ with(packagePolicy) { setAppOpMode(packageName, userId, appOpName, mode) }
}
}
@@ -128,7 +285,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
}
override fun removePackage(packageName: String, userId: Int): Boolean {
- var wasChanged = false
+ var wasChanged: Boolean
service.mutateState {
wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) }
}
@@ -158,6 +315,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
this[AppOpsManager.strOpToOp(op)] = true
}
}
+ if (Flags.runtimePermissionAppopsMappingEnabled()) {
+ foregroundableOps.forEachIndexed { _, op, _ ->
+ if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) {
+ this[op] = true
+ }
+ }
+ }
}
}
@@ -168,6 +332,13 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
this[AppOpsManager.strOpToOp(op)] = true
}
}
+ if (Flags.runtimePermissionAppopsMappingEnabled()) {
+ foregroundableOps.forEachIndexed { _, op, _ ->
+ if (getPackageMode(packageName, op, userId) == AppOpsManager.MODE_FOREGROUND) {
+ this[op] = true
+ }
+ }
+ }
}
}
@@ -189,9 +360,10 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
}
}
- inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() {
+ private inner class OnAppIdAppOpModeChangedListener :
+ AppIdAppOpPolicy.OnAppOpModeChangedListener() {
// (uid, appOpCode) -> newMode
- val pendingChanges = ArrayMap<Pair<Int, Int>, Int>()
+ private val pendingChanges = LongSparseArray<Int>()
override fun onAppOpModeChanged(
appId: Int,
@@ -202,7 +374,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
) {
val uid = UserHandle.getUid(userId, appId)
val appOpCode = AppOpsManager.strOpToOp(appOpName)
- val key = Pair(uid, appOpCode)
+ val key = IntPair.of(uid, appOpCode)
pendingChanges[key] = newMode
}
@@ -211,13 +383,15 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
val listenersLocal = listeners
pendingChanges.forEachIndexed { _, key, mode ->
listenersLocal.forEachIndexed { _, listener ->
- val uid = key.first
- val appOpCode = key.second
+ val uid = IntPair.first(key)
+ val appOpCode = IntPair.second(key)
- listener.onUidModeChanged(uid,
+ listener.onUidModeChanged(
+ uid,
appOpCode,
mode,
- VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ )
}
}
@@ -228,7 +402,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
private inner class OnPackageAppOpModeChangedListener :
PackageAppOpPolicy.OnAppOpModeChangedListener() {
// (packageName, userId, appOpCode) -> newMode
- val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>()
+ private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>()
override fun onAppOpModeChanged(
packageName: String,
@@ -258,4 +432,130 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
pendingChanges.clear()
}
}
+
+ private inner class OnPermissionFlagsChangedListener :
+ AppIdPermissionPolicy.OnPermissionFlagsChangedListener,
+ DevicePermissionPolicy.OnDevicePermissionFlagsChangedListener {
+ // (uid, deviceId, appOpCode) -> newMode
+ private val pendingChanges = ArrayMap<Triple<Int, String, Int>, Int>()
+
+ override fun onPermissionFlagsChanged(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ oldFlags: Int,
+ newFlags: Int
+ ) {
+ onDevicePermissionFlagsChanged(
+ appId,
+ userId,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT,
+ permissionName,
+ oldFlags,
+ newFlags
+ )
+ }
+
+ override fun onDevicePermissionFlagsChanged(
+ appId: Int,
+ userId: Int,
+ deviceId: String,
+ permissionName: String,
+ oldFlags: Int,
+ newFlags: Int
+ ) {
+ backgroundToForegroundPermissionNames[permissionName]?.let { foregroundPermissions ->
+ // This is a background permission; there may be multiple foreground permissions
+ // affected.
+ foregroundPermissions.forEachIndexed { _, foregroundPermissionName ->
+ runtimePermissionNameToAppOp[foregroundPermissionName]?.let { appOpCode ->
+ val foregroundPermissionFlags =
+ getPermissionFlags(appId, userId, foregroundPermissionName)
+ addPendingChangedModeIfNeeded(
+ appId,
+ userId,
+ deviceId,
+ appOpCode,
+ foregroundPermissionFlags,
+ oldFlags,
+ foregroundPermissionFlags,
+ newFlags
+ )
+ }
+ }
+ }
+ ?: foregroundToBackgroundPermissionName[permissionName]?.let { backgroundPermission
+ ->
+ runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
+ val backgroundPermissionFlags =
+ getPermissionFlags(appId, userId, backgroundPermission)
+ addPendingChangedModeIfNeeded(
+ appId,
+ userId,
+ deviceId,
+ appOpCode,
+ oldFlags,
+ backgroundPermissionFlags,
+ newFlags,
+ backgroundPermissionFlags
+ )
+ }
+ }
+ ?: runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
+ addPendingChangedModeIfNeeded(
+ appId,
+ userId,
+ deviceId,
+ appOpCode,
+ oldFlags,
+ PermissionFlags.RUNTIME_GRANTED,
+ newFlags,
+ PermissionFlags.RUNTIME_GRANTED
+ )
+ }
+ }
+
+ private fun getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int =
+ service.getState {
+ with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) }
+ }
+
+ private fun addPendingChangedModeIfNeeded(
+ appId: Int,
+ userId: Int,
+ deviceId: String,
+ appOpCode: Int,
+ oldForegroundFlags: Int,
+ oldBackgroundFlags: Int,
+ newForegroundFlags: Int,
+ newBackgroundFlags: Int,
+ ) {
+ val oldMode = evaluateModeFromPermissionFlags(oldForegroundFlags, oldBackgroundFlags)
+ val newMode = evaluateModeFromPermissionFlags(newForegroundFlags, newBackgroundFlags)
+
+ if (oldMode != newMode) {
+ val uid = UserHandle.getUid(userId, appId)
+ pendingChanges[Triple(uid, deviceId, appOpCode)] = newMode
+ }
+ }
+
+ override fun onStateMutated() {
+ val listenersLocal = listeners
+ pendingChanges.forEachIndexed { _, key, mode ->
+ listenersLocal.forEachIndexed { _, listener ->
+ val uid = key.first
+ val deviceId = key.second
+ val appOpCode = key.third
+
+ listener.onUidModeChanged(uid, appOpCode, mode, deviceId)
+ }
+ }
+
+ pendingChanges.clear()
+ }
+ }
+
+ companion object {
+ private val LOG_TAG = AppOpService::class.java.simpleName
+ }
}
diff --git a/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt b/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt
new file mode 100644
index 000000000000..827dd0e5d292
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.collection
+
+import android.util.LongSparseArray
+
+inline fun <T> LongSparseArray<T>.allIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+ forEachIndexed { index, key, value ->
+ if (!predicate(index, key, value)) {
+ return false
+ }
+ }
+ return true
+}
+
+inline fun <T> LongSparseArray<T>.anyIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+ forEachIndexed { index, key, value ->
+ if (predicate(index, key, value)) {
+ return true
+ }
+ }
+ return false
+}
+
+inline fun <T> LongSparseArray<T>.forEachIndexed(action: (Int, Long, T) -> Unit) {
+ for (index in 0 until size) {
+ action(index, keyAt(index), valueAt(index))
+ }
+}
+
+inline fun <T> LongSparseArray<T>.forEachReversedIndexed(action: (Int, Long, T) -> Unit) {
+ for (index in lastIndex downTo 0) {
+ action(index, keyAt(index), valueAt(index))
+ }
+}
+
+inline fun <T> LongSparseArray<T>.getOrPut(key: Long, defaultValue: () -> T): T {
+ val index = indexOfKey(key)
+ return if (index >= 0) {
+ valueAt(index)
+ } else {
+ defaultValue().also { put(key, it) }
+ }
+}
+
+inline val <T> LongSparseArray<T>.lastIndex: Int
+ get() = size - 1
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun <T> LongSparseArray<T>.minusAssign(key: Long) {
+ delete(key)
+}
+
+inline fun <T> LongSparseArray<T>.noneIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+ forEachIndexed { index, key, value ->
+ if (predicate(index, key, value)) {
+ return false
+ }
+ }
+ return true
+}
+
+inline fun <T> LongSparseArray<T>.removeAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+ var isChanged = false
+ forEachReversedIndexed { index, key, value ->
+ if (predicate(index, key, value)) {
+ removeAt(index)
+ isChanged = true
+ }
+ }
+ return isChanged
+}
+
+inline fun <T> LongSparseArray<T>.retainAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+ var isChanged = false
+ forEachReversedIndexed { index, key, value ->
+ if (!predicate(index, key, value)) {
+ removeAt(index)
+ isChanged = true
+ }
+ }
+ return isChanged
+}
+
+inline val <T> LongSparseArray<T>.size: Int
+ get() = size()
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) {
+ put(key, value)
+}
diff --git a/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt b/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt
new file mode 100644
index 000000000000..a582431aa83c
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.collection
+
+import android.util.SparseIntArray
+
+inline fun SparseIntArray.allIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+ forEachIndexed { index, key, value ->
+ if (!predicate(index, key, value)) {
+ return false
+ }
+ }
+ return true
+}
+
+inline fun SparseIntArray.anyIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+ forEachIndexed { index, key, value ->
+ if (predicate(index, key, value)) {
+ return true
+ }
+ }
+ return false
+}
+
+inline fun SparseIntArray.forEachIndexed(action: (Int, Int, Int) -> Unit) {
+ for (index in 0 until size) {
+ action(index, keyAt(index), valueAt(index))
+ }
+}
+
+inline fun SparseIntArray.forEachReversedIndexed(action: (Int, Int, Int) -> Unit) {
+ for (index in lastIndex downTo 0) {
+ action(index, keyAt(index), valueAt(index))
+ }
+}
+
+inline fun SparseIntArray.getOrPut(key: Int, defaultValue: () -> Int): Int {
+ val index = indexOfKey(key)
+ return if (index >= 0) {
+ valueAt(index)
+ } else {
+ defaultValue().also { put(key, it) }
+ }
+}
+
+inline val SparseIntArray.lastIndex: Int
+ get() = size - 1
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun SparseIntArray.minusAssign(key: Int) {
+ delete(key)
+}
+
+inline fun SparseIntArray.noneIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+ forEachIndexed { index, key, value ->
+ if (predicate(index, key, value)) {
+ return false
+ }
+ }
+ return true
+}
+
+fun SparseIntArray.remove(key: Int) {
+ delete(key)
+}
+
+fun SparseIntArray.remove(key: Int, defaultValue: Int): Int {
+ val index = indexOfKey(key)
+ return if (index >= 0) {
+ val oldValue = valueAt(index)
+ removeAt(index)
+ oldValue
+ } else {
+ defaultValue
+ }
+}
+
+inline fun SparseIntArray.removeAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+ var isChanged = false
+ forEachReversedIndexed { index, key, value ->
+ if (predicate(index, key, value)) {
+ removeAt(index)
+ isChanged = true
+ }
+ }
+ return isChanged
+}
+
+inline fun SparseIntArray.retainAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+ var isChanged = false
+ forEachReversedIndexed { index, key, value ->
+ if (!predicate(index, key, value)) {
+ removeAt(index)
+ isChanged = true
+ }
+ }
+ return isChanged
+}
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun SparseIntArray.set(key: Int, value: Int) {
+ put(key, value)
+}
+
+inline val SparseIntArray.size: Int
+ get() = size()
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 4b086b3aca17..67df67fdf6c1 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -227,25 +227,59 @@ class AppIdPermissionPolicy : SchemePolicy() {
if (isRequestedBySystemPackage) {
return@forEach
}
- val oldFlags = getPermissionFlags(appId, userId, permissionName)
- var newFlags = oldFlags andInv PermissionFlags.UPGRADE_EXEMPT
- val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
- newFlags =
- if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags =
- if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
- setPermissionFlags(appId, userId, permissionName, newFlags)
+ updatePermissionExemptFlags(
+ appId,
+ userId,
+ permission,
+ PermissionFlags.UPGRADE_EXEMPT,
+ 0
+ )
}
}
+ fun MutateStateScope.updatePermissionExemptFlags(
+ appId: Int,
+ userId: Int,
+ permission: Permission,
+ exemptFlagMask: Int,
+ exemptFlagValues: Int
+ ) {
+ val permissionName = permission.name
+ val oldFlags = getPermissionFlags(appId, userId, permissionName)
+ var newFlags = (oldFlags andInv exemptFlagMask) or (exemptFlagValues and exemptFlagMask)
+ if (oldFlags == newFlags) {
+ return
+ }
+ val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags = newFlags or PermissionFlags.RESTRICTION_REVOKED
+ // If the permission was policy fixed as granted but it is no longer on any of the
+ // allowlists we need to clear the policy fixed flag as allowlisting trumps policy i.e.
+ // policy cannot grant a non grantable permission.
+ if (PermissionFlags.isPermissionGranted(oldFlags)) {
+ newFlags = newFlags andInv PermissionFlags.POLICY_FIXED
+ }
+ } else {
+ newFlags = newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (
+ permission.isSoftRestricted && !isExempt &&
+ !anyPackageInAppId(appId) {
+ permissionName in it.androidPackage!!.requestedPermissions &&
+ isSoftRestrictedPermissionExemptForPackage(it, permissionName)
+ }
+ ) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
+ if (oldFlags == newFlags) {
+ return
+ }
+ setPermissionFlags(appId, userId, permissionName, newFlags)
+ }
+
override fun MutateStateScope.onPackageUninstalled(
packageName: String,
appId: Int,
@@ -1118,7 +1152,12 @@ class AppIdPermissionPolicy : SchemePolicy() {
newFlags andInv PermissionFlags.RESTRICTION_REVOKED
}
newFlags =
- if (permission.isSoftRestricted && !isExempt) {
+ if (
+ permission.isSoftRestricted && !isExempt &&
+ !requestingPackageStates.anyIndexed { _, it ->
+ isSoftRestrictedPermissionExemptForPackage(it, permissionName)
+ }
+ ) {
newFlags or PermissionFlags.SOFT_RESTRICTED
} else {
newFlags andInv PermissionFlags.SOFT_RESTRICTED
@@ -1398,6 +1437,17 @@ class AppIdPermissionPolicy : SchemePolicy() {
}
}
+ // See also SoftRestrictedPermissionPolicy.mayGrantPermission()
+ private fun isSoftRestrictedPermissionExemptForPackage(
+ packageState: PackageState,
+ permissionName: String
+ ): Boolean =
+ when (permissionName) {
+ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE ->
+ packageState.androidPackage!!.targetSdkVersion >= Build.VERSION_CODES.Q
+ else -> false
+ }
+
private inline fun MutateStateScope.anyPackageInAppId(
appId: Int,
state: AccessState = newState,
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index b9d89c2184b7..c5c921dc3515 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -346,8 +346,18 @@ object PermissionFlags {
return flags.hasBits(RUNTIME_GRANTED)
}
- fun isAppOpGranted(flags: Int): Boolean =
- isPermissionGranted(flags) && !flags.hasBits(APP_OP_REVOKED)
+ fun isAppOpGranted(flags: Int): Boolean {
+ if (!isPermissionGranted(flags)) {
+ return false
+ }
+ if (flags.hasAnyBit(MASK_RESTRICTED)) {
+ return false
+ }
+ if (flags.hasBits(APP_OP_REVOKED)) {
+ return false
+ }
+ return true
+ }
fun toApiFlags(flags: Int): Int {
var apiFlags = 0
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 0704c8ffca25..0b58543e076d 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -88,7 +88,6 @@ import com.android.server.pm.PackageInstallerService
import com.android.server.pm.PackageManagerLocal
import com.android.server.pm.UserManagerInternal
import com.android.server.pm.UserManagerService
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils
import com.android.server.pm.permission.LegacyPermission
import com.android.server.pm.permission.LegacyPermissionSettings
import com.android.server.pm.permission.LegacyPermissionState
@@ -97,7 +96,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInterface
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
-import com.android.server.policy.SoftRestrictedPermissionPolicy
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.concurrent.CompletableFuture
@@ -1006,25 +1004,14 @@ class PermissionService(private val service: AccessCheckingService) :
}
if (isGranted && oldFlags.hasBits(PermissionFlags.SOFT_RESTRICTED)) {
- // TODO: Refactor SoftRestrictedPermissionPolicy.
- val softRestrictedPermissionPolicy =
- SoftRestrictedPermissionPolicy.forPermission(
- context,
- AndroidPackageUtils.generateAppInfoWithoutState(androidPackage),
- androidPackage,
- UserHandle.of(userId),
- permissionName
+ if (reportError) {
+ Slog.e(
+ LOG_TAG,
+ "$methodName: Cannot grant soft-restricted non-exempt permission" +
+ " $permissionName to package $packageName"
)
- if (!softRestrictedPermissionPolicy.mayGrantPermission()) {
- if (reportError) {
- Slog.e(
- LOG_TAG,
- "$methodName: Cannot grant soft-restricted non-exempt permission" +
- " $permissionName to package $packageName"
- )
- }
- return
}
+ return
}
val newFlags = PermissionFlags.updateRuntimePermissionGranted(oldFlags, isGranted)
@@ -1135,25 +1122,23 @@ class PermissionService(private val service: AccessCheckingService) :
return emptyMap()
}
- val permissionFlagsMap =
- service.getState {
+ service.getState {
+ val permissionFlags =
if (deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) {
with(policy) { getAllPermissionFlags(packageState.appId, userId) }
} else {
with(devicePolicy) {
getAllPermissionFlags(packageState.appId, deviceId, userId)
}
- }
+ } ?: return emptyMap()
+ val permissionStates = ArrayMap<String, PermissionState>()
+ permissionFlags.forEachIndexed { _, permissionName, flags ->
+ val granted = isPermissionGranted(packageState, userId, permissionName, deviceId)
+ val apiFlags = PermissionFlags.toApiFlags(flags)
+ permissionStates[permissionName] = PermissionState(granted, apiFlags)
}
- ?: return emptyMap()
-
- val permissionStates = ArrayMap<String, PermissionState>()
- permissionFlagsMap.forEachIndexed { _, permissionName, flags ->
- val granted = PermissionFlags.isPermissionGranted(flags)
- val apiFlags = PermissionFlags.toApiFlags(flags)
- permissionStates[permissionName] = PermissionState(granted, apiFlags)
+ return permissionStates
}
- return permissionStates
}
override fun isPermissionRevokedByPolicy(
@@ -1852,10 +1837,19 @@ class PermissionService(private val service: AccessCheckingService) :
allowlistedFlags: Int,
userId: Int
) {
+ var exemptMask = 0
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)) {
+ exemptMask = exemptMask or PermissionFlags.SYSTEM_EXEMPT
+ }
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)) {
+ exemptMask = exemptMask or PermissionFlags.UPGRADE_EXEMPT
+ }
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+ exemptMask = exemptMask or PermissionFlags.INSTALLER_EXEMPT
+ }
+
service.mutateState {
with(policy) {
- val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState
-
val permissions = getPermissions()
androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
val permission = permissions[requestedPermission]
@@ -1863,81 +1857,8 @@ class PermissionService(private val service: AccessCheckingService) :
return@forEachIndexed
}
- val oldFlags = permissionsFlags[requestedPermission] ?: 0
- val wasGranted = PermissionFlags.isPermissionGranted(oldFlags)
-
- var newFlags = oldFlags
- var mask = 0
- var allowlistFlagsCopy = allowlistedFlags
- while (allowlistFlagsCopy != 0) {
- val flag = 1 shl allowlistFlagsCopy.countTrailingZeroBits()
- allowlistFlagsCopy = allowlistFlagsCopy and flag.inv()
- when (flag) {
- PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM -> {
- mask = mask or PermissionFlags.SYSTEM_EXEMPT
- newFlags =
- if (permissionNames.contains(requestedPermission)) {
- newFlags or PermissionFlags.SYSTEM_EXEMPT
- } else {
- newFlags andInv PermissionFlags.SYSTEM_EXEMPT
- }
- }
- PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE -> {
- mask = mask or PermissionFlags.UPGRADE_EXEMPT
- newFlags =
- if (permissionNames.contains(requestedPermission)) {
- newFlags or PermissionFlags.UPGRADE_EXEMPT
- } else {
- newFlags andInv PermissionFlags.UPGRADE_EXEMPT
- }
- }
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER -> {
- mask = mask or PermissionFlags.INSTALLER_EXEMPT
- newFlags =
- if (permissionNames.contains(requestedPermission)) {
- newFlags or PermissionFlags.INSTALLER_EXEMPT
- } else {
- newFlags andInv PermissionFlags.INSTALLER_EXEMPT
- }
- }
- }
- }
-
- if (oldFlags == newFlags) {
- return@forEachIndexed
- }
-
- val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
-
- // If the permission is policy fixed as granted but it is no longer
- // on any of the allowlists we need to clear the policy fixed flag
- // as allowlisting trumps policy i.e. policy cannot grant a non
- // grantable permission.
- if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED)) {
- if (!isExempt && wasGranted) {
- mask = mask or PermissionFlags.POLICY_FIXED
- newFlags = newFlags andInv PermissionFlags.POLICY_FIXED
- }
- }
-
- newFlags =
- if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags =
- if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
- mask =
- mask or
- PermissionFlags.RESTRICTION_REVOKED or
- PermissionFlags.SOFT_RESTRICTED
-
- updatePermissionFlags(appId, userId, requestedPermission, mask, newFlags)
+ var exemptFlags = if (requestedPermission in permissionNames) exemptMask else 0
+ updatePermissionExemptFlags(appId, userId, permission, exemptMask, exemptFlags)
}
}
}
@@ -2905,5 +2826,8 @@ class PermissionService(private val service: AccessCheckingService) :
} else {
emptySet<String>()
}
+
+ fun getFullerPermission(permissionName: String): String? =
+ FULLER_PERMISSIONS[permissionName]
}
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 1c71a6287c79..1d225ba09bbd 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -30,7 +30,10 @@ import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SH
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT;
import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.any;
+import static org.mockito.AdditionalMatchers.and;
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -40,6 +43,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Display;
+import android.view.inputmethod.ImeTracker;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -56,7 +60,7 @@ import org.junit.runner.RunWith;
* Test the behavior of {@link DefaultImeVisibilityApplier} when performing or applying the IME
* visibility state.
*
- * Build/Install/Run:
+ * <p>Build/Install/Run:
* atest FrameworksInputMethodSystemServerTests:DefaultImeVisibilityApplierTest
*/
@RunWith(AndroidJUnit4.class)
@@ -75,7 +79,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
public void testPerformShowIme() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.performShowIme(new Binder() /* showInputToken */,
- null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT);
+ ImeTracker.Token.empty(), 0 /* showFlags */, null /* resultReceiver */,
+ SHOW_SOFT_INPUT);
}
verifyShowSoftInput(false, true, 0 /* showFlags */);
}
@@ -84,46 +89,66 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
public void testPerformHideIme() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.performHideIme(new Binder() /* hideInputToken */,
- null /* statsToken */, null, HIDE_SOFT_INPUT);
+ ImeTracker.Token.empty(), null /* resultReceiver */, HIDE_SOFT_INPUT);
}
verifyHideSoftInput(false, true);
}
@Test
public void testApplyImeVisibility_throwForInvalidState() {
- assertThrows(IllegalArgumentException.class,
- () -> mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID));
+ assertThrows(IllegalArgumentException.class, () -> {
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_INVALID);
+ }
+ });
}
@Test
public void testApplyImeVisibility_showIme() {
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME);
- verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), any());
+ final var statsToken = ImeTracker.Token.empty();
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME);
+ }
+ verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken));
}
@Test
public void testApplyImeVisibility_hideIme() {
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME);
- verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt(), any());
+ final var statsToken = ImeTracker.Token.empty();
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+ }
+ verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */,
+ eq(statsToken));
}
@Test
public void testApplyImeVisibility_hideImeExplicit() throws Exception {
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_EXPLICIT);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_HIDE_IME_EXPLICIT);
+ }
verifyHideSoftInput(true, true);
}
@Test
public void testApplyImeVisibility_hideNotAlways() throws Exception {
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_NOT_ALWAYS);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_HIDE_IME_NOT_ALWAYS);
+ }
verifyHideSoftInput(true, true);
}
@Test
public void testApplyImeVisibility_showImeImplicit() throws Exception {
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_SHOW_IME_IMPLICIT);
+ }
verifyShowSoftInput(true, true, 0 /* showFlags */);
}
@@ -135,21 +160,21 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
mInputMethodManagerService.setAttachedClientForTesting(null);
startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
// Verify hideIme will apply the expected displayId when the default IME
// visibility applier app STATE_HIDE_IME.
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
- eq(mWindowToken), eq(displayIdToShowIme), eq(null));
+ eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken));
}
}
@Test
public void testShowImeScreenshot() {
synchronized (ImfLock.class) {
- mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY,
- null /* statsToken */);
+ mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY);
}
verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken),
@@ -174,17 +199,20 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
synchronized (ImfLock.class) {
// Simulate the system hides the IME when switching IME services in different users.
// (e.g. unbinding the IME from the current user to the profile user)
+ final var statsToken = ImeTracker.Token.empty();
final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
- mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, null, 0, null,
+ mInputMethodManagerService.hideCurrentInputLocked(mWindowToken,
+ statsToken, 0 /* flags */, null /* resultReceiver */,
HIDE_SWITCH_USER);
mInputMethodManagerService.onUnbindCurrentMethodByReset();
// Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing
// the IME hidden state.
- verify(mVisibilityApplier).applyImeVisibility(eq(mWindowToken), any(),
- eq(STATE_HIDE_IME));
+ // The unbind will cancel the previous stats token, and create a new one internally.
+ verify(mVisibilityApplier).applyImeVisibility(
+ eq(mWindowToken), any(), eq(STATE_HIDE_IME));
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
- eq(mWindowToken), eq(displayIdToShowIme), eq(null));
+ eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
}
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index fae5f86e4007..a22cacbcb5df 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -39,9 +39,12 @@ import static com.android.server.inputmethod.InputMethodManagerService.ImeDispla
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.notNull;
+
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -58,7 +61,7 @@ import org.mockito.ArgumentCaptor;
* Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when
* requesting the IME visibility.
*
- * Build/Install/Run:
+ * <p> Build/Install/Run:
* atest FrameworksInputMethodSystemServerTests:ImeVisibilityStateComputerTest
*/
@RunWith(AndroidJUnit4.class)
@@ -91,7 +94,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showImplicit() {
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+ InputMethodManager.SHOW_IMPLICIT);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -106,7 +110,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showExplicit() {
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -125,7 +129,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showExplicit_thenShowImplicit() {
initImeTargetWindowState(mWindowToken);
- mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
assertThat(mComputer.mRequestedShowExplicitly).isTrue();
mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
@@ -139,10 +143,10 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showForced_thenShowExplicit() {
initImeTargetWindowState(mWindowToken);
- mComputer.onImeShowFlags(null, InputMethodManager.SHOW_FORCED);
+ mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_FORCED);
assertThat(mComputer.mShowForced).isTrue();
- mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
assertThat(mComputer.mShowForced).isTrue();
}
@@ -152,7 +156,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN);
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+ InputMethodManager.SHOW_IMPLICIT);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -170,7 +175,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+ InputMethodManager.SHOW_IMPLICIT);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -188,7 +194,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
mComputer.setInputShown(true);
initImeTargetWindowState(mWindowToken);
- assertThat(mComputer.canHideIme(null, InputMethodManager.HIDE_NOT_ALWAYS)).isTrue();
+ assertThat(mComputer.canHideIme(ImeTracker.Token.empty(),
+ InputMethodManager.HIDE_NOT_ALWAYS)).isTrue();
mComputer.requestImeVisibility(mWindowToken, false);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -281,7 +288,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass(
ImeVisibilityResult.class);
verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(),
- resultCaptor.capture());
+ notNull() /* statsToken */, resultCaptor.capture());
final IBinder imeInputTarget = targetCaptor.getValue();
final ImeVisibilityResult result = resultCaptor.getValue();
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index a1be00aab340..f4d95afaacf4 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -277,8 +278,9 @@ public class InputMethodManagerServiceTestBase {
.setCurrentMethodVisible();
}
verify(mMockInputMethod, times(showSoftInput ? 1 : 0))
- .showSoftInput(any(), any(),
- showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt(), any());
+ .showSoftInput(any() /* showInputToken */ , notNull() /* statsToken */,
+ showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt() /* flags*/,
+ any() /* resultReceiver */);
}
protected void verifyHideSoftInput(boolean setNotVisible, boolean hideSoftInput)
@@ -288,6 +290,7 @@ public class InputMethodManagerServiceTestBase {
.setCurrentMethodNotVisible();
}
verify(mMockInputMethod, times(hideSoftInput ? 1 : 0))
- .hideSoftInput(any(), any(), anyInt(), any());
+ .hideSoftInput(any() /* hideInputToken */, notNull() /* statsToken */,
+ anyInt() /* flags */, any() /* resultReceiver */);
}
}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
index cde46abafe95..96753b6d2bcc 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -233,24 +233,6 @@ class AppIdPermissionPolicyTest : BasePermissionPolicyTest() {
.isEqualTo(expectedNewFlags)
}
- @Test
- fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
- val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.INSTALLER_EXEMPT
- testOnPackageInstalled(
- oldFlags,
- permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
- ) {}
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALLER_EXEMPT
- assertWithMessage(
- "After onPackageInstalled() is called for a non-system app that requests a runtime" +
- " soft restricted permission that is exempted. The actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
private fun testOnPackageInstalled(
oldFlags: Int,
permissionInfoFlags: Int = 0,
diff --git a/services/tests/VpnTests/Android.bp b/services/tests/VpnTests/Android.bp
new file mode 100644
index 000000000000..64a9a3b4f119
--- /dev/null
+++ b/services/tests/VpnTests/Android.bp
@@ -0,0 +1,39 @@
+//########################################################################
+// Build FrameworksVpnTests package
+//########################################################################
+package {
+ default_team: "trendy_team_fwk_core_networking",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "Android-Apache-2.0"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "FrameworksVpnTests",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.kt",
+ ],
+
+ defaults: ["framework-connectivity-test-defaults"],
+ test_suites: ["device-tests"],
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "framework-protos",
+ "mockito-target-minus-junit4",
+ "net-tests-utils",
+ "platform-test-annotations",
+ "services.core",
+ "cts-net-utils",
+ "service-connectivity-tiramisu-pre-jarjar",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+}
diff --git a/services/tests/VpnTests/AndroidManifest.xml b/services/tests/VpnTests/AndroidManifest.xml
new file mode 100644
index 000000000000..d884084f2eb7
--- /dev/null
+++ b/services/tests/VpnTests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.tests.vpn">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.tests.vpn"
+ android:label="Frameworks VPN Tests" />
+</manifest> \ No newline at end of file
diff --git a/services/tests/VpnTests/AndroidTest.xml b/services/tests/VpnTests/AndroidTest.xml
new file mode 100644
index 000000000000..ebeeac7d269b
--- /dev/null
+++ b/services/tests/VpnTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs VPN Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksVpnTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksVpnTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.vpn" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration> \ No newline at end of file
diff --git a/services/tests/VpnTests/OWNERS b/services/tests/VpnTests/OWNERS
new file mode 100644
index 000000000000..45ea251a02e4
--- /dev/null
+++ b/services/tests/VpnTests/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking \ No newline at end of file
diff --git a/services/tests/VpnTests/java/android/net/Ikev2VpnProfileTest.java b/services/tests/VpnTests/java/android/net/Ikev2VpnProfileTest.java
new file mode 100644
index 000000000000..180f54e1cf5f
--- /dev/null
+++ b/services/tests/VpnTests/java/android/net/Ikev2VpnProfileTest.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V6;
+import static android.net.cts.util.IkeSessionTestUtils.getTestIkeSessionParams;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.net.VpnProfile;
+import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
+import com.android.net.module.util.ProxyUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.auth.x500.X500Principal;
+
+/** Unit tests for {@link Ikev2VpnProfile.Builder}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class Ikev2VpnProfileTest {
+ private static final String SERVER_ADDR_STRING = "1.2.3.4";
+ private static final String IDENTITY_STRING = "Identity";
+ private static final String USERNAME_STRING = "username";
+ private static final String PASSWORD_STRING = "pa55w0rd";
+ private static final String EXCL_LIST = "exclList";
+ private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+ private static final int TEST_MTU = 1300;
+
+ private final ProxyInfo mProxy = ProxyInfo.buildDirectProxy(
+ SERVER_ADDR_STRING, -1, ProxyUtils.exclusionStringAsList(EXCL_LIST));
+
+ private X509Certificate mUserCert;
+ private X509Certificate mServerRootCa;
+ private PrivateKey mPrivateKey;
+
+ @Before
+ public void setUp() throws Exception {
+ mServerRootCa = generateRandomCertAndKeyPair().cert;
+
+ final CertificateAndKey userCertKey = generateRandomCertAndKeyPair();
+ mUserCert = userCertKey.cert;
+ mPrivateKey = userCertKey.key;
+ }
+
+ private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() {
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING);
+
+ builder.setBypassable(true);
+ builder.setProxy(mProxy);
+ builder.setMaxMtu(TEST_MTU);
+ builder.setMetered(true);
+
+ return builder;
+ }
+
+ @Test
+ public void testBuildValidProfileWithOptions() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ // Check non-auth parameters correctly stored
+ assertEquals(SERVER_ADDR_STRING, profile.getServerAddr());
+ assertEquals(IDENTITY_STRING, profile.getUserIdentity());
+ assertEquals(mProxy, profile.getProxyInfo());
+ assertTrue(profile.isBypassable());
+ assertTrue(profile.isMetered());
+ assertEquals(TEST_MTU, profile.getMaxMtu());
+ assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms());
+ }
+
+ @Test
+ public void testBuildUsernamePasswordProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(USERNAME_STRING, profile.getUsername());
+ assertEquals(PASSWORD_STRING, profile.getPassword());
+ assertEquals(mServerRootCa, profile.getServerRootCaCert());
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildDigitalSignatureProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(profile.getUserCert(), mUserCert);
+ assertEquals(mPrivateKey, profile.getRsaPrivateKey());
+ assertEquals(profile.getServerRootCaCert(), mServerRootCa);
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ }
+
+ @Test
+ public void testBuildPresharedKeyProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertArrayEquals(PSK_BYTES, profile.getPresharedKey());
+
+ assertNull(profile.getServerRootCaCert());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildWithAllowedAlgorithmsAead() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthPsk(PSK_BYTES);
+
+ List<String> allowedAlgorithms =
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305);
+ builder.setAllowedAlgorithms(allowedAlgorithms);
+
+ final Ikev2VpnProfile profile = builder.build();
+ assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+ }
+
+ @Test
+ public void testBuildWithAllowedAlgorithmsNormal() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthPsk(PSK_BYTES);
+
+ List<String> allowedAlgorithms =
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.AUTH_AES_XCBC,
+ IpSecAlgorithm.AUTH_AES_CMAC,
+ IpSecAlgorithm.CRYPT_AES_CBC,
+ IpSecAlgorithm.CRYPT_AES_CTR);
+ builder.setAllowedAlgorithms(allowedAlgorithms);
+
+ final Ikev2VpnProfile profile = builder.build();
+ assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+ }
+
+ @Test
+ public void testSetAllowedAlgorithmsEmptyList() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setAllowedAlgorithms(new ArrayList<>());
+ fail("Expected exception due to no valid algorithm set");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetAllowedAlgorithmsInvalidList() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256));
+ fail("Expected exception due to missing encryption");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC));
+ fail("Expected exception due to missing authentication");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5));
+ fail("Expected exception due to insecure algorithm");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1));
+ fail("Expected exception due to insecure algorithm");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testBuildNoAuthMethodSet() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.build();
+ fail("Expected exception due to lack of auth method");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testBuildExcludeLocalRoutesSet() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthPsk(PSK_BYTES);
+ builder.setLocalRoutesExcluded(true);
+
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+ assertTrue(profile.areLocalRoutesExcluded());
+
+ builder.setBypassable(false);
+ try {
+ builder.build();
+ fail("Expected exception because excludeLocalRoutes should be set only"
+ + " on the bypassable VPN");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testBuildInvalidMtu() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setMaxMtu(500);
+ fail("Expected exception due to too-small MTU");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private void verifyVpnProfileCommon(VpnProfile profile) {
+ assertEquals(SERVER_ADDR_STRING, profile.server);
+ assertEquals(IDENTITY_STRING, profile.ipsecIdentifier);
+ assertEquals(mProxy, profile.proxy);
+ assertTrue(profile.isBypassable);
+ assertTrue(profile.isMetered);
+ assertEquals(TEST_MTU, profile.maxMtu);
+ }
+
+ @Test
+ public void testPskConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecCaCert);
+ }
+
+ @Test
+ public void testUsernamePasswordConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(USERNAME_STRING, profile.username);
+ assertEquals(PASSWORD_STRING, profile.password);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecSecret);
+ }
+
+ @Test
+ public void testRsaConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE
+ + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded());
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
+ assertEquals(
+ expectedSecret,
+ profile.ipsecSecret);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ }
+
+ @Test
+ public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+ profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ assertNull(result.getServerRootCaCert());
+ }
+
+ @Test
+ public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.ipsecSecret = new String(PSK_BYTES);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getPresharedKey());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ }
+
+ @Test
+ public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getPresharedKey());
+ }
+
+ @Test
+ public void testPskConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testUsernamePasswordConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testRsaConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testBuildWithIkeTunConnParamsConvertToVpnProfile() throws Exception {
+ // Special keyId that contains delimiter character of VpnProfile
+ final byte[] keyId = "foo\0bar".getBytes();
+ final IkeTunnelConnectionParams tunnelParams = new IkeTunnelConnectionParams(
+ getTestIkeSessionParams(true /* testIpv6 */, new IkeKeyIdIdentification(keyId)),
+ CHILD_PARAMS);
+ final Ikev2VpnProfile ikev2VpnProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
+ final VpnProfile vpnProfile = ikev2VpnProfile.toVpnProfile();
+
+ assertEquals(VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS, vpnProfile.type);
+
+ // Username, password, server, ipsecIdentifier, ipsecCaCert, ipsecSecret, ipsecUserCert and
+ // getAllowedAlgorithms should not be set if IkeTunnelConnectionParams is set.
+ assertEquals("", vpnProfile.server);
+ assertEquals("", vpnProfile.ipsecIdentifier);
+ assertEquals("", vpnProfile.username);
+ assertEquals("", vpnProfile.password);
+ assertEquals("", vpnProfile.ipsecCaCert);
+ assertEquals("", vpnProfile.ipsecSecret);
+ assertEquals("", vpnProfile.ipsecUserCert);
+ assertEquals(0, vpnProfile.getAllowedAlgorithms().size());
+
+ // IkeTunnelConnectionParams should stay the same.
+ assertEquals(tunnelParams, vpnProfile.ikeTunConnParams);
+
+ // Convert to disk-stable format and then back to Ikev2VpnProfile should be the same.
+ final VpnProfile decodedVpnProfile =
+ VpnProfile.decode(vpnProfile.key, vpnProfile.encode());
+ final Ikev2VpnProfile convertedIkev2VpnProfile =
+ Ikev2VpnProfile.fromVpnProfile(decodedVpnProfile);
+ assertEquals(ikev2VpnProfile, convertedIkev2VpnProfile);
+ }
+
+ @Test
+ public void testConversionIsLosslessWithIkeTunConnParams() throws Exception {
+ final IkeTunnelConnectionParams tunnelParams =
+ new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
+ // Config authentication related fields is not required while building with
+ // IkeTunnelConnectionParams.
+ final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testAutomaticNattAndIpVersionConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAutomaticNattKeepaliveTimerEnabled(true);
+ builder.setAutomaticIpVersionSelectionEnabled(true);
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testAutomaticNattAndIpVersionDefaults() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(false, ikeProfile.isAutomaticNattKeepaliveTimerEnabled());
+ assertEquals(false, ikeProfile.isAutomaticIpVersionSelectionEnabled());
+ }
+
+ @Test
+ public void testEquals() throws Exception {
+ // Verify building without IkeTunnelConnectionParams
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ assertEquals(builder.build(), builder.build());
+
+ // Verify building with IkeTunnelConnectionParams
+ final IkeTunnelConnectionParams tunnelParams =
+ new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
+ final IkeTunnelConnectionParams tunnelParams2 =
+ new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
+ assertEquals(new Ikev2VpnProfile.Builder(tunnelParams).build(),
+ new Ikev2VpnProfile.Builder(tunnelParams2).build());
+ }
+
+ @Test
+ public void testBuildProfileWithNullProxy() throws Exception {
+ final Ikev2VpnProfile ikev2VpnProfile =
+ new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
+ .setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa)
+ .build();
+
+ // ProxyInfo should be null for the profile without setting ProxyInfo.
+ assertNull(ikev2VpnProfile.getProxyInfo());
+
+ // ProxyInfo should stay null after performing toVpnProfile() and fromVpnProfile()
+ final VpnProfile vpnProfile = ikev2VpnProfile.toVpnProfile();
+ assertNull(vpnProfile.proxy);
+
+ final Ikev2VpnProfile convertedIkev2VpnProfile = Ikev2VpnProfile.fromVpnProfile(vpnProfile);
+ assertNull(convertedIkev2VpnProfile.getProxyInfo());
+ }
+
+ private static class CertificateAndKey {
+ public final X509Certificate cert;
+ public final PrivateKey key;
+
+ CertificateAndKey(X509Certificate cert, PrivateKey key) {
+ this.cert = cert;
+ this.key = key;
+ }
+ }
+
+ private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception {
+ final Date validityBeginDate =
+ new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L));
+ final Date validityEndDate =
+ new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L));
+
+ // Generate a keypair
+ final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(512);
+ final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ final X500Principal dnName = new X500Principal("CN=test.android.com");
+ final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+ certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+ certGen.setSubjectDN(dnName);
+ certGen.setIssuerDN(dnName);
+ certGen.setNotBefore(validityBeginDate);
+ certGen.setNotAfter(validityEndDate);
+ certGen.setPublicKey(keyPair.getPublic());
+ certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+ final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL");
+ return new CertificateAndKey(cert, keyPair.getPrivate());
+ }
+}
diff --git a/services/tests/VpnTests/java/android/net/VpnManagerTest.java b/services/tests/VpnTests/java/android/net/VpnManagerTest.java
new file mode 100644
index 000000000000..f5b83f0ee79d
--- /dev/null
+++ b/services/tests/VpnTests/java/android/net/VpnManagerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 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.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.test.mock.MockContext;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.MessageUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link VpnManager}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VpnManagerTest {
+
+ private static final String PKG_NAME = "fooPackage";
+
+ private static final String SERVER_ADDR_STRING = "1.2.3.4";
+ private static final String IDENTITY_STRING = "Identity";
+ private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+
+ private IVpnManager mMockService;
+ private VpnManager mVpnManager;
+ private final MockContext mMockContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return PKG_NAME;
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ assumeFalse("Skipping test because watches don't support VPN",
+ InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH));
+ mMockService = mock(IVpnManager.class);
+ mVpnManager = new VpnManager(mMockContext, mMockService);
+ }
+
+ @Test
+ public void testProvisionVpnProfilePreconsented() throws Exception {
+ final PlatformVpnProfile profile = getPlatformVpnProfile();
+ when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
+ .thenReturn(true);
+
+ // Expect there to be no intent returned, as consent has already been granted.
+ assertNull(mVpnManager.provisionVpnProfile(profile));
+ verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+ }
+
+ @Test
+ public void testProvisionVpnProfileNeedsConsent() throws Exception {
+ final PlatformVpnProfile profile = getPlatformVpnProfile();
+ when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
+ .thenReturn(false);
+
+ // Expect intent to be returned, as consent has not already been granted.
+ final Intent intent = mVpnManager.provisionVpnProfile(profile);
+ assertNotNull(intent);
+
+ final ComponentName expectedComponentName =
+ ComponentName.unflattenFromString(
+ "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
+ assertEquals(expectedComponentName, intent.getComponent());
+ verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+ }
+
+ @Test
+ public void testDeleteProvisionedVpnProfile() throws Exception {
+ mVpnManager.deleteProvisionedVpnProfile();
+ verify(mMockService).deleteVpnProfile(eq(PKG_NAME));
+ }
+
+ @Test
+ public void testStartProvisionedVpnProfile() throws Exception {
+ mVpnManager.startProvisionedVpnProfile();
+ verify(mMockService).startVpnProfile(eq(PKG_NAME));
+ }
+
+ @Test
+ public void testStopProvisionedVpnProfile() throws Exception {
+ mVpnManager.stopProvisionedVpnProfile();
+ verify(mMockService).stopVpnProfile(eq(PKG_NAME));
+ }
+
+ private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
+ return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
+ .setBypassable(true)
+ .setMaxMtu(1300)
+ .setMetered(true)
+ .setAuthPsk(PSK_BYTES)
+ .build();
+ }
+
+ @Test
+ public void testVpnTypesEqual() throws Exception {
+ SparseArray<String> vmVpnTypes = MessageUtils.findMessageNames(
+ new Class[] { VpnManager.class }, new String[]{ "TYPE_VPN_" });
+ SparseArray<String> nativeVpnType = MessageUtils.findMessageNames(
+ new Class[] { NativeVpnType.class }, new String[]{ "" });
+
+ // TYPE_VPN_NONE = -1 is only defined in VpnManager.
+ assertEquals(vmVpnTypes.size() - 1, nativeVpnType.size());
+ for (int i = VpnManager.TYPE_VPN_SERVICE; i < vmVpnTypes.size(); i++) {
+ assertEquals(vmVpnTypes.get(i), "TYPE_VPN_" + nativeVpnType.get(i));
+ }
+ }
+}
diff --git a/services/tests/VpnTests/java/com/android/internal/net/VpnProfileTest.java b/services/tests/VpnTests/java/com/android/internal/net/VpnProfileTest.java
new file mode 100644
index 000000000000..acbe8b858d8f
--- /dev/null
+++ b/services/tests/VpnTests/java/com/android/internal/net/VpnProfileTest.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net;
+
+import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V4;
+
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.IpSecAlgorithm;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** Unit tests for {@link VpnProfile}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VpnProfileTest {
+ private static final String DUMMY_PROFILE_KEY = "Test";
+
+ private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
+ private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
+ private static final int ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE = 25;
+ private static final int ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION = 26;
+ private static final int ENCODED_INDEX_IKE_TUN_CONN_PARAMS = 27;
+ private static final int ENCODED_INDEX_AUTOMATIC_NATT_KEEPALIVE_TIMER_ENABLED = 28;
+ private static final int ENCODED_INDEX_AUTOMATIC_IP_VERSION_SELECTION_ENABLED = 29;
+
+ @Test
+ public void testDefaults() throws Exception {
+ final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
+
+ assertEquals(DUMMY_PROFILE_KEY, p.key);
+ assertEquals("", p.name);
+ assertEquals(VpnProfile.TYPE_PPTP, p.type);
+ assertEquals("", p.server);
+ assertEquals("", p.username);
+ assertEquals("", p.password);
+ assertEquals("", p.dnsServers);
+ assertEquals("", p.searchDomains);
+ assertEquals("", p.routes);
+ assertTrue(p.mppe);
+ assertEquals("", p.l2tpSecret);
+ assertEquals("", p.ipsecIdentifier);
+ assertEquals("", p.ipsecSecret);
+ assertEquals("", p.ipsecUserCert);
+ assertEquals("", p.ipsecCaCert);
+ assertEquals("", p.ipsecServerCert);
+ assertEquals(null, p.proxy);
+ assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
+ assertFalse(p.isBypassable);
+ assertFalse(p.isMetered);
+ assertEquals(1360, p.maxMtu);
+ assertFalse(p.areAuthParamsInline);
+ assertFalse(p.isRestrictedToTestNetworks);
+ assertFalse(p.excludeLocalRoutes);
+ assertFalse(p.requiresInternetValidation);
+ assertFalse(p.automaticNattKeepaliveTimerEnabled);
+ assertFalse(p.automaticIpVersionSelectionEnabled);
+ }
+
+ private VpnProfile getSampleIkev2Profile(String key) {
+ final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
+ false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
+ null /* ikeTunConnParams */, true /* mAutomaticNattKeepaliveTimerEnabled */,
+ true /* automaticIpVersionSelectionEnabled */);
+
+ p.name = "foo";
+ p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ p.server = "bar";
+ p.username = "baz";
+ p.password = "qux";
+ p.dnsServers = "8.8.8.8";
+ p.searchDomains = "";
+ p.routes = "0.0.0.0/0";
+ p.mppe = false;
+ p.l2tpSecret = "";
+ p.ipsecIdentifier = "quux";
+ p.ipsecSecret = "quuz";
+ p.ipsecUserCert = "corge";
+ p.ipsecCaCert = "grault";
+ p.ipsecServerCert = "garply";
+ p.proxy = null;
+ p.setAllowedAlgorithms(
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.CRYPT_AES_CBC));
+ p.isBypassable = true;
+ p.isMetered = true;
+ p.maxMtu = 1350;
+ p.areAuthParamsInline = true;
+
+ // Not saved, but also not compared.
+ p.saveLogin = true;
+
+ return p;
+ }
+
+ private VpnProfile getSampleIkev2ProfileWithIkeTunConnParams(String key) {
+ final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
+ false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
+ new IkeTunnelConnectionParams(IKE_PARAMS_V4, CHILD_PARAMS),
+ true /* mAutomaticNattKeepaliveTimerEnabled */,
+ true /* automaticIpVersionSelectionEnabled */);
+
+ p.name = "foo";
+ p.server = "bar";
+ p.dnsServers = "8.8.8.8";
+ p.searchDomains = "";
+ p.routes = "0.0.0.0/0";
+ p.mppe = false;
+ p.proxy = null;
+ p.setAllowedAlgorithms(
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.CRYPT_AES_CBC));
+ p.isBypassable = true;
+ p.isMetered = true;
+ p.maxMtu = 1350;
+ p.areAuthParamsInline = true;
+
+ // Not saved, but also not compared.
+ p.saveLogin = true;
+
+ return p;
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(
+ getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY));
+
+ final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ modified.maxMtu--;
+ assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 28);
+ assertParcelSane(getSampleIkev2ProfileWithIkeTunConnParams(DUMMY_PROFILE_KEY), 28);
+ }
+
+ @Test
+ public void testEncodeDecodeWithIkeTunConnParams() {
+ final VpnProfile profile = getSampleIkev2ProfileWithIkeTunConnParams(DUMMY_PROFILE_KEY);
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertEquals(profile, decoded);
+ }
+
+ @Test
+ public void testEncodeDecode() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertEquals(profile, decoded);
+ }
+
+ @Test
+ public void testEncodeDecodeTooManyValues() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final byte[] tooManyValues =
+ (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes();
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
+ }
+
+ private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
+ // Sort to ensure when we remove, we can do it from greatest first.
+ Arrays.sort(missingIndices);
+
+ final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
+ final List<String> parts =
+ new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
+
+ // Remove from back first to ensure indexing is consistent.
+ for (int i = missingIndices.length - 1; i >= 0; i--) {
+ parts.remove(missingIndices[i]);
+ }
+
+ return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
+ }
+
+ @Test
+ public void testEncodeDecodeInvalidNumberOfValues() {
+ final String tooFewValues =
+ getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_AUTH_PARAMS_INLINE,
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS,
+ ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE,
+ ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION,
+ ENCODED_INDEX_IKE_TUN_CONN_PARAMS,
+ ENCODED_INDEX_AUTOMATIC_NATT_KEEPALIVE_TIMER_ENABLED,
+ ENCODED_INDEX_AUTOMATIC_IP_VERSION_SELECTION_ENABLED
+ /* missingIndices */);
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
+ }
+
+ private String getEncodedDecodedIkev2ProfileWithtooFewValues() {
+ return getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS,
+ ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE,
+ ENCODED_INDEX_REQUIRE_PLATFORM_VALIDATION,
+ ENCODED_INDEX_IKE_TUN_CONN_PARAMS,
+ ENCODED_INDEX_AUTOMATIC_NATT_KEEPALIVE_TIMER_ENABLED,
+ ENCODED_INDEX_AUTOMATIC_IP_VERSION_SELECTION_ENABLED /* missingIndices */);
+ }
+
+ @Test
+ public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
+ final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+ // Verify decoding without isRestrictedToTestNetworks defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.isRestrictedToTestNetworks);
+ }
+
+ @Test
+ public void testEncodeDecodeMissingExcludeLocalRoutes() {
+ final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+ // Verify decoding without excludeLocalRoutes defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.excludeLocalRoutes);
+ }
+
+ @Test
+ public void testEncodeDecodeMissingRequiresValidation() {
+ final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+ // Verify decoding without requiresValidation defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.requiresInternetValidation);
+ }
+
+ @Test
+ public void testEncodeDecodeMissingAutomaticNattKeepaliveTimerEnabled() {
+ final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+ // Verify decoding without automaticNattKeepaliveTimerEnabled defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.automaticNattKeepaliveTimerEnabled);
+ }
+
+ @Test
+ public void testEncodeDecodeMissingAutomaticIpVersionSelectionEnabled() {
+ final String tooFewValues = getEncodedDecodedIkev2ProfileWithtooFewValues();
+
+ // Verify decoding without automaticIpVersionSelectionEnabled defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.automaticIpVersionSelectionEnabled);
+ }
+
+ @Test
+ public void testEncodeDecodeLoginsNotSaved() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ profile.saveLogin = false;
+
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertNotEquals(profile, decoded);
+
+ // Add the username/password back, everything else must be equal.
+ decoded.username = profile.username;
+ decoded.password = profile.password;
+ assertEquals(profile, decoded);
+ }
+
+ @Test
+ public void testClone() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final VpnProfile clone = profile.clone();
+ assertEquals(profile, clone);
+ assertNotSame(profile, clone);
+ }
+}
diff --git a/services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java b/services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java
new file mode 100644
index 000000000000..ecc70e3669d6
--- /dev/null
+++ b/services/tests/VpnTests/java/com/android/server/VpnManagerServiceTest.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.testutils.ContextUtils.mockService;
+import static com.android.testutils.MiscAsserts.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.UserIdInt;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.INetd;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.Credentials;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.net.VpnProfile;
+import com.android.server.connectivity.Vpn;
+import com.android.server.connectivity.VpnProfileStore;
+import com.android.server.net.LockdownVpnTracker;
+import com.android.testutils.HandlerUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VpnManagerServiceTest extends VpnTestBase {
+ private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER";
+
+ private static final int TIMEOUT_MS = 2_000;
+
+ @Mock Context mContext;
+ @Mock Context mContextWithoutAttributionTag;
+ @Mock Context mSystemContext;
+ @Mock Context mUserAllContext;
+ private HandlerThread mHandlerThread;
+ @Mock private Vpn mVpn;
+ @Mock private INetworkManagementService mNms;
+ @Mock private ConnectivityManager mCm;
+ @Mock private UserManager mUserManager;
+ @Mock private INetd mNetd;
+ @Mock private PackageManager mPackageManager;
+ @Mock private VpnProfileStore mVpnProfileStore;
+ @Mock private LockdownVpnTracker mLockdownVpnTracker;
+
+ private VpnManagerServiceDependencies mDeps;
+ private VpnManagerService mService;
+ private BroadcastReceiver mUserPresentReceiver;
+ private BroadcastReceiver mIntentReceiver;
+ private final String mNotMyVpnPkg = "com.not.my.vpn";
+
+ class VpnManagerServiceDependencies extends VpnManagerService.Dependencies {
+ @Override
+ public HandlerThread makeHandlerThread() {
+ return mHandlerThread;
+ }
+
+ @Override
+ public INetworkManagementService getINetworkManagementService() {
+ return mNms;
+ }
+
+ @Override
+ public INetd getNetd() {
+ return mNetd;
+ }
+
+ @Override
+ public Vpn createVpn(Looper looper, Context context, INetworkManagementService nms,
+ INetd netd, @UserIdInt int userId) {
+ return mVpn;
+ }
+
+ @Override
+ public VpnProfileStore getVpnProfileStore() {
+ return mVpnProfileStore;
+ }
+
+ @Override
+ public LockdownVpnTracker createLockDownVpnTracker(Context context, Handler handler,
+ Vpn vpn, VpnProfile profile) {
+ return mLockdownVpnTracker;
+ }
+
+ @Override
+ public @UserIdInt int getMainUserId() {
+ return UserHandle.USER_SYSTEM;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mHandlerThread = new HandlerThread("TestVpnManagerService");
+ mDeps = new VpnManagerServiceDependencies();
+
+ // The attribution tag is a dependency for IKE library to collect VPN metrics correctly
+ // and thus should not be changed without updating the IKE code.
+ doReturn(mContext)
+ .when(mContextWithoutAttributionTag)
+ .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
+
+ doReturn(mUserAllContext).when(mContext).createContextAsUser(UserHandle.ALL, 0);
+ doReturn(mSystemContext).when(mContext).createContextAsUser(UserHandle.SYSTEM, 0);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ setMockedPackages(mPackageManager, sPackages);
+
+ mockService(mContext, ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mCm);
+ mockService(mContext, UserManager.class, Context.USER_SERVICE, mUserManager);
+ doReturn(SYSTEM_USER).when(mUserManager).getUserInfo(eq(SYSTEM_USER_ID));
+
+ mService = new VpnManagerService(mContextWithoutAttributionTag, mDeps);
+ mService.systemReady();
+
+ final ArgumentCaptor<BroadcastReceiver> intentReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ final ArgumentCaptor<BroadcastReceiver> userPresentReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mSystemContext).registerReceiver(
+ userPresentReceiverCaptor.capture(), any(), any(), any());
+ verify(mUserAllContext, times(2)).registerReceiver(
+ intentReceiverCaptor.capture(), any(), any(), any());
+ mUserPresentReceiver = userPresentReceiverCaptor.getValue();
+ mIntentReceiver = intentReceiverCaptor.getValue();
+
+ // Add user to create vpn in mVpn
+ onUserStarted(SYSTEM_USER_ID);
+ assertNotNull(mService.mVpns.get(SYSTEM_USER_ID));
+ }
+
+ @Test
+ public void testUpdateAppExclusionList() {
+ // Start vpn
+ mService.startVpnProfile(TEST_VPN_PKG);
+ verify(mVpn).startVpnProfile(eq(TEST_VPN_PKG));
+
+ // Remove package due to package replaced.
+ onPackageRemoved(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
+ verify(mVpn, never()).refreshPlatformVpnAppExclusionList();
+
+ // Add package due to package replaced.
+ onPackageAdded(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
+ verify(mVpn, never()).refreshPlatformVpnAppExclusionList();
+
+ // Remove package
+ onPackageRemoved(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
+ verify(mVpn).refreshPlatformVpnAppExclusionList();
+
+ // Add the package back
+ onPackageAdded(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
+ verify(mVpn, times(2)).refreshPlatformVpnAppExclusionList();
+ }
+
+ @Test
+ public void testStartVpnProfileFromDiffPackage() {
+ assertThrows(
+ SecurityException.class, () -> mService.startVpnProfile(mNotMyVpnPkg));
+ }
+
+ @Test
+ public void testStopVpnProfileFromDiffPackage() {
+ assertThrows(SecurityException.class, () -> mService.stopVpnProfile(mNotMyVpnPkg));
+ }
+
+ @Test
+ public void testGetProvisionedVpnProfileStateFromDiffPackage() {
+ assertThrows(SecurityException.class, () ->
+ mService.getProvisionedVpnProfileState(mNotMyVpnPkg));
+ }
+
+ @Test
+ public void testGetProvisionedVpnProfileState() {
+ mService.getProvisionedVpnProfileState(TEST_VPN_PKG);
+ verify(mVpn).getProvisionedVpnProfileState(TEST_VPN_PKG);
+ }
+
+ private Intent buildIntent(String action, String packageName, int userId, int uid,
+ boolean isReplacing) {
+ final Intent intent = new Intent(action);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(Intent.EXTRA_REPLACING, isReplacing);
+ if (packageName != null) {
+ intent.setData(Uri.fromParts("package" /* scheme */, packageName, null /* fragment */));
+ }
+
+ return intent;
+ }
+
+ private void sendIntent(Intent intent) {
+ sendIntent(mIntentReceiver, mContext, intent);
+ }
+
+ private void sendIntent(BroadcastReceiver receiver, Context context, Intent intent) {
+ final Handler h = mHandlerThread.getThreadHandler();
+
+ // Send in handler thread.
+ h.post(() -> receiver.onReceive(context, intent));
+ HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS);
+ }
+
+ private void onUserStarted(int userId) {
+ sendIntent(buildIntent(Intent.ACTION_USER_STARTED,
+ null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
+ }
+
+ private void onUserUnlocked(int userId) {
+ sendIntent(buildIntent(Intent.ACTION_USER_UNLOCKED,
+ null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
+ }
+
+ private void onUserStopped(int userId) {
+ sendIntent(buildIntent(Intent.ACTION_USER_STOPPED,
+ null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
+ }
+
+ private void onLockDownReset() {
+ sendIntent(buildIntent(LockdownVpnTracker.ACTION_LOCKDOWN_RESET, null /* packageName */,
+ UserHandle.USER_SYSTEM, -1 /* uid */, false /* isReplacing */));
+ }
+
+ private void onPackageAdded(String packageName, int userId, int uid, boolean isReplacing) {
+ sendIntent(buildIntent(Intent.ACTION_PACKAGE_ADDED, packageName, userId, uid, isReplacing));
+ }
+
+ private void onPackageAdded(String packageName, int uid, boolean isReplacing) {
+ onPackageAdded(packageName, UserHandle.USER_SYSTEM, uid, isReplacing);
+ }
+
+ private void onPackageRemoved(String packageName, int userId, int uid, boolean isReplacing) {
+ sendIntent(buildIntent(Intent.ACTION_PACKAGE_REMOVED, packageName, userId, uid,
+ isReplacing));
+ }
+
+ private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
+ onPackageRemoved(packageName, UserHandle.USER_SYSTEM, uid, isReplacing);
+ }
+
+ @Test
+ public void testReceiveIntentFromNonHandlerThread() {
+ assertThrows(IllegalStateException.class, () ->
+ mIntentReceiver.onReceive(mContext, buildIntent(Intent.ACTION_PACKAGE_REMOVED,
+ PKGS[0], UserHandle.USER_SYSTEM, PKG_UIDS[0], true /* isReplacing */)));
+
+ assertThrows(IllegalStateException.class, () ->
+ mUserPresentReceiver.onReceive(mContext, new Intent(Intent.ACTION_USER_PRESENT)));
+ }
+
+ private void setupLockdownVpn(String packageName) {
+ final byte[] profileTag = packageName.getBytes(StandardCharsets.UTF_8);
+ doReturn(profileTag).when(mVpnProfileStore).get(Credentials.LOCKDOWN_VPN);
+ }
+
+ private void setupVpnProfile(String profileName) {
+ final VpnProfile profile = new VpnProfile(profileName);
+ profile.name = profileName;
+ profile.server = "192.0.2.1";
+ profile.dnsServers = "8.8.8.8";
+ profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
+ final byte[] encodedProfile = profile.encode();
+ doReturn(encodedProfile).when(mVpnProfileStore).get(Credentials.VPN + profileName);
+ }
+
+ @Test
+ public void testUserPresent() {
+ // Verify that LockDownVpnTracker is not created.
+ verify(mLockdownVpnTracker, never()).init();
+
+ setupLockdownVpn(TEST_VPN_PKG);
+ setupVpnProfile(TEST_VPN_PKG);
+
+ // mUserPresentReceiver only registers ACTION_USER_PRESENT intent and does no verification
+ // on action, so an empty intent is enough.
+ sendIntent(mUserPresentReceiver, mSystemContext, new Intent());
+
+ verify(mLockdownVpnTracker).init();
+ verify(mSystemContext).unregisterReceiver(mUserPresentReceiver);
+ verify(mUserAllContext, never()).unregisterReceiver(any());
+ }
+
+ @Test
+ public void testUpdateLockdownVpn() {
+ setupLockdownVpn(TEST_VPN_PKG);
+ onUserUnlocked(SYSTEM_USER_ID);
+
+ // Will not create lockDownVpnTracker w/o valid profile configured in the keystore
+ verify(mLockdownVpnTracker, never()).init();
+
+ setupVpnProfile(TEST_VPN_PKG);
+
+ // Remove the user from mVpns
+ onUserStopped(SYSTEM_USER_ID);
+ onUserUnlocked(SYSTEM_USER_ID);
+ verify(mLockdownVpnTracker, never()).init();
+
+ // Add user back
+ onUserStarted(SYSTEM_USER_ID);
+ verify(mLockdownVpnTracker).init();
+
+ // Trigger another update. The existing LockDownVpnTracker should be shut down and
+ // initialize another one.
+ onUserUnlocked(SYSTEM_USER_ID);
+ verify(mLockdownVpnTracker).shutdown();
+ verify(mLockdownVpnTracker, times(2)).init();
+ }
+
+ @Test
+ public void testLockdownReset() {
+ // Init LockdownVpnTracker
+ setupLockdownVpn(TEST_VPN_PKG);
+ setupVpnProfile(TEST_VPN_PKG);
+ onUserUnlocked(SYSTEM_USER_ID);
+ verify(mLockdownVpnTracker).init();
+
+ onLockDownReset();
+ verify(mLockdownVpnTracker).reset();
+ }
+
+ @Test
+ public void testLockdownResetWhenLockdownVpnTrackerIsNotInit() {
+ setupLockdownVpn(TEST_VPN_PKG);
+ setupVpnProfile(TEST_VPN_PKG);
+
+ onLockDownReset();
+
+ // LockDownVpnTracker is not created. Lockdown reset will not take effect.
+ verify(mLockdownVpnTracker, never()).reset();
+ }
+
+ @Test
+ public void testIsVpnLockdownEnabled() {
+ // Vpn is created but the VPN lockdown is not enabled.
+ assertFalse(mService.isVpnLockdownEnabled(SYSTEM_USER_ID));
+
+ // Set lockdown for the SYSTEM_USER_ID VPN.
+ doReturn(true).when(mVpn).getLockdown();
+ assertTrue(mService.isVpnLockdownEnabled(SYSTEM_USER_ID));
+
+ // Even lockdown is enabled but no Vpn is created for SECONDARY_USER.
+ assertFalse(mService.isVpnLockdownEnabled(SECONDARY_USER.id));
+ }
+
+ @Test
+ public void testGetVpnLockdownAllowlist() {
+ doReturn(null).when(mVpn).getLockdownAllowlist();
+ assertNull(mService.getVpnLockdownAllowlist(SYSTEM_USER_ID));
+
+ final List<String> expected = List.of(PKGS);
+ doReturn(expected).when(mVpn).getLockdownAllowlist();
+ assertEquals(expected, mService.getVpnLockdownAllowlist(SYSTEM_USER_ID));
+
+ // Even lockdown is enabled but no Vpn is created for SECONDARY_USER.
+ assertNull(mService.getVpnLockdownAllowlist(SECONDARY_USER.id));
+ }
+}
diff --git a/services/tests/VpnTests/java/com/android/server/VpnTestBase.java b/services/tests/VpnTests/java/com/android/server/VpnTestBase.java
new file mode 100644
index 000000000000..6113872e213f
--- /dev/null
+++ b/services/tests/VpnTests/java/com/android/server/VpnTestBase.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.content.pm.UserInfo.FLAG_ADMIN;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PRIMARY;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/** Common variables or methods shared between VpnTest and VpnManagerServiceTest. */
+public class VpnTestBase {
+ protected static final String TEST_VPN_PKG = "com.testvpn.vpn";
+ /**
+ * Names and UIDs for some fake packages. Important points:
+ * - UID is ordered increasing.
+ * - One pair of packages have consecutive UIDs.
+ */
+ protected static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"};
+ protected static final int[] PKG_UIDS = {10066, 10077, 10078, 10400};
+ // Mock packages
+ protected static final Map<String, Integer> sPackages = new ArrayMap<>();
+ static {
+ for (int i = 0; i < PKGS.length; i++) {
+ sPackages.put(PKGS[i], PKG_UIDS[i]);
+ }
+ sPackages.put(TEST_VPN_PKG, Process.myUid());
+ }
+
+ // Mock users
+ protected static final int SYSTEM_USER_ID = 0;
+ protected static final UserInfo SYSTEM_USER = new UserInfo(0, "system", UserInfo.FLAG_PRIMARY);
+ protected static final UserInfo PRIMARY_USER = new UserInfo(27, "Primary",
+ FLAG_ADMIN | FLAG_PRIMARY);
+ protected static final UserInfo SECONDARY_USER = new UserInfo(15, "Secondary", FLAG_ADMIN);
+ protected static final UserInfo RESTRICTED_PROFILE_A = new UserInfo(40, "RestrictedA",
+ FLAG_RESTRICTED);
+ protected static final UserInfo RESTRICTED_PROFILE_B = new UserInfo(42, "RestrictedB",
+ FLAG_RESTRICTED);
+ protected static final UserInfo MANAGED_PROFILE_A = new UserInfo(45, "ManagedA",
+ FLAG_MANAGED_PROFILE);
+ static {
+ RESTRICTED_PROFILE_A.restrictedProfileParentId = PRIMARY_USER.id;
+ RESTRICTED_PROFILE_B.restrictedProfileParentId = SECONDARY_USER.id;
+ MANAGED_PROFILE_A.profileGroupId = PRIMARY_USER.id;
+ }
+
+ // Populate a fake packageName-to-UID mapping.
+ protected void setMockedPackages(PackageManager mockPm, final Map<String, Integer> packages) {
+ try {
+ doAnswer(invocation -> {
+ final String appName = (String) invocation.getArguments()[0];
+ final int userId = (int) invocation.getArguments()[1];
+
+ final Integer appId = packages.get(appName);
+ if (appId == null) {
+ throw new PackageManager.NameNotFoundException(appName);
+ }
+
+ return UserHandle.getUid(userId, appId);
+ }).when(mockPm).getPackageUidAsUser(anyString(), anyInt());
+ } catch (Exception e) {
+ }
+ }
+
+ protected List<Integer> toList(int[] arr) {
+ return Arrays.stream(arr).boxed().collect(Collectors.toList());
+ }
+}
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
new file mode 100644
index 000000000000..9115f952b724
--- /dev/null
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -0,0 +1,3293 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.Manifest.permission.BIND_VPN_SERVICE;
+import static android.Manifest.permission.CONTROL_VPN;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
+import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.INetd.IF_STATE_DOWN;
+import static android.net.INetd.IF_STATE_UP;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.net.VpnManager.TYPE_VPN_PLATFORM;
+import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.TEST_IDENTITY;
+import static android.net.cts.util.IkeSessionTestUtils.TEST_KEEPALIVE_TIMEOUT_UNSET;
+import static android.net.cts.util.IkeSessionTestUtils.getTestIkeSessionParams;
+import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_NONE;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_UDP;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV4;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV6;
+import static android.os.UserHandle.PER_USER_RANGE;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_PREFERRED_IKE_PROTOCOL_INT;
+
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+import static com.android.server.connectivity.Vpn.AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+import static com.android.server.connectivity.Vpn.DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC;
+import static com.android.server.connectivity.Vpn.DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
+import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_AUTO;
+import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV4_UDP;
+import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_ESP;
+import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_UDP;
+import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor;
+import static com.android.testutils.MiscAsserts.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.longThat;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.ConnectivityManager;
+import android.net.INetd;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
+import android.net.InterfaceConfigurationParcel;
+import android.net.IpPrefix;
+import android.net.IpSecConfig;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo.DetailedState;
+import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.UidRangeParcel;
+import android.net.VpnManager;
+import android.net.VpnProfileState;
+import android.net.VpnService;
+import android.net.VpnTransportInfo;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
+import android.net.ipsec.ike.exceptions.IkeNonProtocolException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.net.ipsec.ike.exceptions.IkeTimeoutException;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.INetworkManagementService;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.PowerWhitelistManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.security.Credentials;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Range;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.DeviceIdleInternal;
+import com.android.server.IpSecService;
+import com.android.server.VpnTestBase;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tests for {@link Vpn}.
+ *
+ * Build, install and run with:
+ * runtest frameworks-net -c com.android.server.connectivity.VpnTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VpnTest extends VpnTestBase {
+ private static final String TAG = "VpnTest";
+
+ static final Network EGRESS_NETWORK = new Network(101);
+ static final String EGRESS_IFACE = "wlan0";
+ private static final String TEST_VPN_CLIENT = "2.4.6.8";
+ private static final String TEST_VPN_SERVER = "1.2.3.4";
+ private static final String TEST_VPN_IDENTITY = "identity";
+ private static final byte[] TEST_VPN_PSK = "psk".getBytes();
+
+ private static final int IP4_PREFIX_LEN = 32;
+ private static final int IP6_PREFIX_LEN = 64;
+ private static final int MIN_PORT = 0;
+ private static final int MAX_PORT = 65535;
+
+ private static final InetAddress TEST_VPN_CLIENT_IP =
+ InetAddresses.parseNumericAddress(TEST_VPN_CLIENT);
+ private static final InetAddress TEST_VPN_SERVER_IP =
+ InetAddresses.parseNumericAddress(TEST_VPN_SERVER);
+ private static final InetAddress TEST_VPN_CLIENT_IP_2 =
+ InetAddresses.parseNumericAddress("192.0.2.200");
+ private static final InetAddress TEST_VPN_SERVER_IP_2 =
+ InetAddresses.parseNumericAddress("192.0.2.201");
+ private static final InetAddress TEST_VPN_INTERNAL_IP =
+ InetAddresses.parseNumericAddress("198.51.100.10");
+ private static final InetAddress TEST_VPN_INTERNAL_IP6 =
+ InetAddresses.parseNumericAddress("2001:db8::1");
+ private static final InetAddress TEST_VPN_INTERNAL_DNS =
+ InetAddresses.parseNumericAddress("8.8.8.8");
+ private static final InetAddress TEST_VPN_INTERNAL_DNS6 =
+ InetAddresses.parseNumericAddress("2001:4860:4860::8888");
+
+ private static final IkeTrafficSelector IN_TS =
+ new IkeTrafficSelector(MIN_PORT, MAX_PORT, TEST_VPN_INTERNAL_IP, TEST_VPN_INTERNAL_IP);
+ private static final IkeTrafficSelector IN_TS6 =
+ new IkeTrafficSelector(
+ MIN_PORT, MAX_PORT, TEST_VPN_INTERNAL_IP6, TEST_VPN_INTERNAL_IP6);
+ private static final IkeTrafficSelector OUT_TS =
+ new IkeTrafficSelector(MIN_PORT, MAX_PORT,
+ InetAddresses.parseNumericAddress("0.0.0.0"),
+ InetAddresses.parseNumericAddress("255.255.255.255"));
+ private static final IkeTrafficSelector OUT_TS6 =
+ new IkeTrafficSelector(
+ MIN_PORT,
+ MAX_PORT,
+ InetAddresses.parseNumericAddress("::"),
+ InetAddresses.parseNumericAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+
+ private static final Network TEST_NETWORK = new Network(Integer.MAX_VALUE);
+ private static final Network TEST_NETWORK_2 = new Network(Integer.MAX_VALUE - 1);
+ private static final String TEST_IFACE_NAME = "TEST_IFACE";
+ private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345;
+ private static final long TEST_TIMEOUT_MS = 500L;
+ private static final long TIMEOUT_CROSSTHREAD_MS = 20_000L;
+ private static final String PRIMARY_USER_APP_EXCLUDE_KEY =
+ "VPNAPPEXCLUDED_27_com.testvpn.vpn";
+ static final String PKGS_BYTES = getPackageByteString(List.of(PKGS));
+ private static final Range<Integer> PRIMARY_USER_RANGE = uidRangeForUser(PRIMARY_USER.id);
+ private static final int TEST_KEEPALIVE_TIMER = 800;
+ private static final int TEST_SUB_ID = 1234;
+ private static final String TEST_MCCMNC = "12345";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
+ @Mock private UserManager mUserManager;
+ @Mock private PackageManager mPackageManager;
+ @Mock private INetworkManagementService mNetService;
+ @Mock private INetd mNetd;
+ @Mock private AppOpsManager mAppOps;
+ @Mock private NotificationManager mNotificationManager;
+ @Mock private Vpn.SystemServices mSystemServices;
+ @Mock private Vpn.IkeSessionWrapper mIkeSessionWrapper;
+ @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
+ @Mock private Vpn.VpnNetworkAgentWrapper mMockNetworkAgent;
+ @Mock private ConnectivityManager mConnectivityManager;
+ @Mock private ConnectivityDiagnosticsManager mCdm;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private TelephonyManager mTmPerSub;
+ @Mock private CarrierConfigManager mConfigManager;
+ @Mock private SubscriptionManager mSubscriptionManager;
+ @Mock private IpSecService mIpSecService;
+ @Mock private VpnProfileStore mVpnProfileStore;
+ private final TestExecutor mExecutor;
+ @Mock DeviceIdleInternal mDeviceIdleInternal;
+ private final VpnProfile mVpnProfile;
+
+ @Captor private ArgumentCaptor<Collection<Range<Integer>>> mUidRangesCaptor;
+
+ private IpSecManager mIpSecManager;
+ private TestDeps mTestDeps;
+
+ public static class TestExecutor extends ScheduledThreadPoolExecutor {
+ public static final long REAL_DELAY = -1;
+
+ // For the purposes of the test, run all scheduled tasks after 10ms to save
+ // execution time, unless overridden by the specific test. Set to REAL_DELAY
+ // to actually wait for the delay specified by the real call to schedule().
+ public long delayMs = 10;
+ // If this is true, execute() will call the runnable inline. This is useful because
+ // super.execute() calls schedule(), which messes with checks that scheduled() is
+ // called a given number of times.
+ public boolean executeDirect = false;
+
+ public TestExecutor() {
+ super(1);
+ }
+
+ @Override
+ public void execute(final Runnable command) {
+ // See |executeDirect| for why this is necessary.
+ if (executeDirect) {
+ command.run();
+ } else {
+ super.execute(command);
+ }
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(final Runnable command, final long delay,
+ TimeUnit unit) {
+ if (0 == delay || delayMs == REAL_DELAY) {
+ // super.execute() calls schedule() with 0, so use the real delay if it's 0.
+ return super.schedule(command, delay, unit);
+ } else {
+ return super.schedule(command, delayMs, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+
+ public VpnTest() throws Exception {
+ // Build an actual VPN profile that is capable of being converted to and from an
+ // Ikev2VpnProfile
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY);
+ builder.setAuthPsk(TEST_VPN_PSK);
+ builder.setBypassable(true /* isBypassable */);
+ mExecutor = spy(new TestExecutor());
+ mVpnProfile = builder.build().toVpnProfile();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mIpSecManager = new IpSecManager(mContext, mIpSecService);
+ mTestDeps = spy(new TestDeps());
+ doReturn(IPV6_MIN_MTU)
+ .when(mTestDeps)
+ .calculateVpnMtu(any(), anyInt(), anyInt(), anyBoolean());
+ doReturn(1500).when(mTestDeps).getJavaNetworkInterfaceMtu(any(), anyInt());
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ setMockedPackages(sPackages);
+
+ when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+ mockService(UserManager.class, Context.USER_SERVICE, mUserManager);
+ mockService(AppOpsManager.class, Context.APP_OPS_SERVICE, mAppOps);
+ mockService(NotificationManager.class, Context.NOTIFICATION_SERVICE, mNotificationManager);
+ mockService(ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mConnectivityManager);
+ mockService(IpSecManager.class, Context.IPSEC_SERVICE, mIpSecManager);
+ mockService(ConnectivityDiagnosticsManager.class, Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ mCdm);
+ mockService(TelephonyManager.class, Context.TELEPHONY_SERVICE, mTelephonyManager);
+ mockService(CarrierConfigManager.class, Context.CARRIER_CONFIG_SERVICE, mConfigManager);
+ mockService(SubscriptionManager.class, Context.TELEPHONY_SUBSCRIPTION_SERVICE,
+ mSubscriptionManager);
+ doReturn(mTmPerSub).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
+ .thenReturn(Resources.getSystem().getString(
+ R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+ .thenReturn(true);
+
+ // Used by {@link Notification.Builder}
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
+ when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+
+ doNothing().when(mNetService).registerObserver(any());
+
+ // Deny all appops by default.
+ when(mAppOps.noteOpNoThrow(anyString(), anyInt(), anyString(), any(), any()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+
+ // Setup IpSecService
+ final IpSecTunnelInterfaceResponse tunnelResp =
+ new IpSecTunnelInterfaceResponse(
+ IpSecManager.Status.OK, TEST_TUNNEL_RESOURCE_ID, TEST_IFACE_NAME);
+ when(mIpSecService.createTunnelInterface(any(), any(), any(), any(), any()))
+ .thenReturn(tunnelResp);
+ doReturn(new LinkProperties()).when(mConnectivityManager).getLinkProperties(any());
+
+ // The unit test should know what kind of permission it needs and set the permission by
+ // itself, so set the default value of Context#checkCallingOrSelfPermission to
+ // PERMISSION_DENIED.
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(any());
+
+ // Set up mIkev2SessionCreator and mExecutor
+ resetIkev2SessionCreator(mIkeSessionWrapper);
+ }
+
+ private void resetIkev2SessionCreator(Vpn.IkeSessionWrapper ikeSession) {
+ reset(mIkev2SessionCreator);
+ when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any()))
+ .thenReturn(ikeSession);
+ }
+
+ private <T> void mockService(Class<T> clazz, String name, T service) {
+ doReturn(service).when(mContext).getSystemService(name);
+ doReturn(name).when(mContext).getSystemServiceName(clazz);
+ if (mContext.getSystemService(clazz).getClass().equals(Object.class)) {
+ // Test is using mockito-extended (mContext uses Answers.RETURNS_DEEP_STUBS and returned
+ // a mock object on a final method)
+ doCallRealMethod().when(mContext).getSystemService(clazz);
+ }
+ }
+
+ private Set<Range<Integer>> rangeSet(Range<Integer> ... ranges) {
+ final Set<Range<Integer>> range = new ArraySet<>();
+ for (Range<Integer> r : ranges) range.add(r);
+
+ return range;
+ }
+
+ private static Range<Integer> uidRangeForUser(int userId) {
+ return new Range<Integer>(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
+ }
+
+ private Range<Integer> uidRange(int start, int stop) {
+ return new Range<Integer>(start, stop);
+ }
+
+ private static String getPackageByteString(List<String> packages) {
+ try {
+ return HexDump.toHexString(
+ PersistableBundleUtils.toDiskStableBytes(PersistableBundleUtils.fromList(
+ packages, PersistableBundleUtils.STRING_SERIALIZER)),
+ true /* upperCase */);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ @Test
+ public void testRestrictedProfilesAreAddedToVpn() {
+ setMockedUsers(PRIMARY_USER, SECONDARY_USER, RESTRICTED_PROFILE_A, RESTRICTED_PROFILE_B);
+
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+
+ // Assume the user can have restricted profiles.
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ final Set<Range<Integer>> ranges =
+ vpn.createUserAndRestrictedProfilesRanges(PRIMARY_USER.id, null, null);
+
+ assertEquals(rangeSet(PRIMARY_USER_RANGE, uidRangeForUser(RESTRICTED_PROFILE_A.id)),
+ ranges);
+ }
+
+ @Test
+ public void testManagedProfilesAreNotAddedToVpn() {
+ setMockedUsers(PRIMARY_USER, MANAGED_PROFILE_A);
+
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final Set<Range<Integer>> ranges = vpn.createUserAndRestrictedProfilesRanges(
+ PRIMARY_USER.id, null, null);
+
+ assertEquals(rangeSet(PRIMARY_USER_RANGE), ranges);
+ }
+
+ @Test
+ public void testAddUserToVpnOnlyAddsOneUser() {
+ setMockedUsers(PRIMARY_USER, RESTRICTED_PROFILE_A, MANAGED_PROFILE_A);
+
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final Set<Range<Integer>> ranges = new ArraySet<>();
+ vpn.addUserToRanges(ranges, PRIMARY_USER.id, null, null);
+
+ assertEquals(rangeSet(PRIMARY_USER_RANGE), ranges);
+ }
+
+ @Test
+ public void testUidAllowAndDenylist() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final Range<Integer> user = PRIMARY_USER_RANGE;
+ final int userStart = user.getLower();
+ final int userStop = user.getUpper();
+ final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
+
+ // Allowed list
+ final Set<Range<Integer>> allow = vpn.createUserAndRestrictedProfilesRanges(PRIMARY_USER.id,
+ Arrays.asList(packages), null /* disallowedApplications */);
+ assertEquals(rangeSet(
+ uidRange(userStart + PKG_UIDS[0], userStart + PKG_UIDS[0]),
+ uidRange(userStart + PKG_UIDS[1], userStart + PKG_UIDS[2]),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[0]),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0])),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[1]),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[2]))),
+ allow);
+
+ // Denied list
+ final Set<Range<Integer>> disallow =
+ vpn.createUserAndRestrictedProfilesRanges(PRIMARY_USER.id,
+ null /* allowedApplications */, Arrays.asList(packages));
+ assertEquals(rangeSet(
+ uidRange(userStart, userStart + PKG_UIDS[0] - 1),
+ uidRange(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
+ /* Empty range between UIDS[1] and UIDS[2], should be excluded, */
+ uidRange(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)),
+ disallow);
+ }
+
+ private void verifyPowerSaveTempWhitelistApp(String packageName) {
+ verify(mDeviceIdleInternal, timeout(TEST_TIMEOUT_MS)).addPowerSaveTempWhitelistApp(
+ anyInt(), eq(packageName), anyLong(), anyInt(), eq(false),
+ eq(PowerWhitelistManager.REASON_VPN), eq("VpnManager event"));
+ }
+
+ @Test
+ public void testGetAlwaysAndOnGetLockDown() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+
+ // Default state.
+ assertFalse(vpn.getAlwaysOn());
+ assertFalse(vpn.getLockdown());
+
+ // Set always-on without lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
+ assertTrue(vpn.getAlwaysOn());
+ assertFalse(vpn.getLockdown());
+
+ // Set always-on with lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
+ assertTrue(vpn.getAlwaysOn());
+ assertTrue(vpn.getLockdown());
+
+ // Remove always-on configuration.
+ assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
+ assertFalse(vpn.getAlwaysOn());
+ assertFalse(vpn.getLockdown());
+ }
+
+ @Test
+ public void testAlwaysOnWithoutLockdown() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], false /* lockdown */, null /* lockdownAllowlist */));
+ verify(mConnectivityManager, never()).setRequireVpnForUids(anyBoolean(), any());
+
+ assertTrue(vpn.setAlwaysOnPackage(
+ null /* packageName */, false /* lockdown */, null /* lockdownAllowlist */));
+ verify(mConnectivityManager, never()).setRequireVpnForUids(anyBoolean(), any());
+ }
+
+ @Test
+ public void testLockdownChangingPackage() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final Range<Integer> user = PRIMARY_USER_RANGE;
+ final int userStart = user.getLower();
+ final int userStop = user.getUpper();
+ // Set always-on without lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
+
+ // Set always-on with lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
+ }));
+
+ // Switch to another app.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
+ }));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart, userStart + PKG_UIDS[3] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[3] + 1), userStop)
+ }));
+ }
+
+ @Test
+ public void testLockdownAllowlist() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final Range<Integer> user = PRIMARY_USER_RANGE;
+ final int userStart = user.getLower();
+ final int userStop = user.getUpper();
+ // Set always-on with lockdown and allow app PKGS[2] from lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[2])));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1]) - 1),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)
+ }));
+ // Change allowed app list to PKGS[3].
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[3])));
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)
+ }));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[3] + 1), userStop)
+ }));
+
+ // Change the VPN app.
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[3])));
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1))
+ }));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart, userStart + PKG_UIDS[0] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1))
+ }));
+
+ // Remove the list of allowed packages.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[3] + 1), userStop)
+ }));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1), userStop),
+ }));
+
+ // Add the list of allowed packages.
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[1])));
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1), userStop),
+ }));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
+ }));
+
+ // Try allowing a package with a comma, should be rejected.
+ assertFalse(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList("a.b,c.d")));
+
+ // Pass a non-existent packages in the allowlist, they (and only they) should be ignored.
+ // allowed package should change from PGKS[1] to PKGS[2].
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
+ }));
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[2] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[2] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)
+ }));
+ }
+
+ @Test
+ public void testLockdownSystemUser() throws Exception {
+ final Vpn vpn = createVpn(SYSTEM_USER_ID);
+
+ // Uid 0 is always excluded and PKG_UIDS[1] is the uid of the VPN.
+ final List<Integer> excludedUids = new ArrayList<>(List.of(0, PKG_UIDS[1]));
+ final List<Range<Integer>> ranges = makeVpnUidRange(SYSTEM_USER_ID, excludedUids);
+
+ // Set always-on with lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true /* lockdown */, null /* lockdownAllowlist */));
+ verify(mConnectivityManager).setRequireVpnForUids(true, ranges);
+
+ // Disable always-on with lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(
+ null /* packageName */, false /* lockdown */, null /* lockdownAllowlist */));
+ verify(mConnectivityManager).setRequireVpnForUids(false, ranges);
+
+ // Set always-on with lockdown and allow the app PKGS[2].
+ excludedUids.add(PKG_UIDS[2]);
+ final List<Range<Integer>> ranges2 = makeVpnUidRange(SYSTEM_USER_ID, excludedUids);
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true /* lockdown */, Collections.singletonList(PKGS[2])));
+ verify(mConnectivityManager).setRequireVpnForUids(true, ranges2);
+
+ // Disable always-on with lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(
+ null /* packageName */, false /* lockdown */, null /* lockdownAllowlist */));
+ verify(mConnectivityManager).setRequireVpnForUids(false, ranges2);
+ }
+
+ @Test
+ public void testLockdownRuleRepeatability() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
+ new UidRangeParcel(PRIMARY_USER_RANGE.getLower(), PRIMARY_USER_RANGE.getUpper())};
+ // Given legacy lockdown is already enabled,
+ vpn.setLockdown(true);
+ verify(mConnectivityManager, times(1)).setRequireVpnForUids(true,
+ toRanges(primaryUserRangeParcel));
+
+ // Enabling legacy lockdown twice should do nothing.
+ vpn.setLockdown(true);
+ verify(mConnectivityManager, times(1)).setRequireVpnForUids(anyBoolean(), any());
+
+ // And disabling should remove the rules exactly once.
+ vpn.setLockdown(false);
+ verify(mConnectivityManager, times(1)).setRequireVpnForUids(false,
+ toRanges(primaryUserRangeParcel));
+
+ // Removing the lockdown again should have no effect.
+ vpn.setLockdown(false);
+ verify(mConnectivityManager, times(2)).setRequireVpnForUids(anyBoolean(), any());
+ }
+
+ private ArrayList<Range<Integer>> toRanges(UidRangeParcel[] ranges) {
+ ArrayList<Range<Integer>> rangesArray = new ArrayList<>(ranges.length);
+ for (int i = 0; i < ranges.length; i++) {
+ rangesArray.add(new Range<>(ranges[i].start, ranges[i].stop));
+ }
+ return rangesArray;
+ }
+
+ @Test
+ public void testLockdownRuleReversibility() throws Exception {
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final UidRangeParcel[] entireUser = {
+ new UidRangeParcel(PRIMARY_USER_RANGE.getLower(), PRIMARY_USER_RANGE.getUpper())
+ };
+ final UidRangeParcel[] exceptPkg0 = {
+ new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
+ new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1,
+ Process.toSdkSandboxUid(entireUser[0].start + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(entireUser[0].start + PKG_UIDS[0] + 1),
+ entireUser[0].stop),
+ };
+
+ final InOrder order = inOrder(mConnectivityManager);
+
+ // Given lockdown is enabled with no package (legacy VPN),
+ vpn.setLockdown(true);
+ order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser));
+
+ // When a new VPN package is set the rules should change to cover that package.
+ vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
+ order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(entireUser));
+ order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(exceptPkg0));
+
+ // When that VPN package is unset, everything should be undone again in reverse.
+ vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
+ order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(exceptPkg0));
+ order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser));
+ }
+
+ @Test
+ public void testOnUserAddedAndRemoved_restrictedUser() throws Exception {
+ final InOrder order = inOrder(mMockNetworkAgent);
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final Set<Range<Integer>> initialRange = rangeSet(PRIMARY_USER_RANGE);
+ // Note since mVpnProfile is a Ikev2VpnProfile, this starts an IkeV2VpnRunner.
+ startLegacyVpn(vpn, mVpnProfile);
+ // Set an initial Uid range and mock the network agent
+ vpn.mNetworkCapabilities.setUids(initialRange);
+ vpn.mNetworkAgent = mMockNetworkAgent;
+
+ // Add the restricted user
+ setMockedUsers(PRIMARY_USER, RESTRICTED_PROFILE_A);
+ vpn.onUserAdded(RESTRICTED_PROFILE_A.id);
+ // Expect restricted user range to be added to the NetworkCapabilities.
+ final Set<Range<Integer>> expectRestrictedRange =
+ rangeSet(PRIMARY_USER_RANGE, uidRangeForUser(RESTRICTED_PROFILE_A.id));
+ assertEquals(expectRestrictedRange, vpn.mNetworkCapabilities.getUids());
+ order.verify(mMockNetworkAgent).doSendNetworkCapabilities(
+ argThat(nc -> expectRestrictedRange.equals(nc.getUids())));
+
+ // Remove the restricted user
+ vpn.onUserRemoved(RESTRICTED_PROFILE_A.id);
+ // Expect restricted user range to be removed from the NetworkCapabilities.
+ assertEquals(initialRange, vpn.mNetworkCapabilities.getUids());
+ order.verify(mMockNetworkAgent).doSendNetworkCapabilities(
+ argThat(nc -> initialRange.equals(nc.getUids())));
+ }
+
+ @Test
+ public void testOnUserAddedAndRemoved_restrictedUserLockdown() throws Exception {
+ final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
+ new UidRangeParcel(PRIMARY_USER_RANGE.getLower(), PRIMARY_USER_RANGE.getUpper())};
+ final Range<Integer> restrictedUserRange = uidRangeForUser(RESTRICTED_PROFILE_A.id);
+ final UidRangeParcel[] restrictedUserRangeParcel = new UidRangeParcel[] {
+ new UidRangeParcel(restrictedUserRange.getLower(), restrictedUserRange.getUpper())};
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+
+ // Set lockdown calls setRequireVpnForUids
+ vpn.setLockdown(true);
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(primaryUserRangeParcel));
+
+ // Add the restricted user
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ setMockedUsers(PRIMARY_USER, RESTRICTED_PROFILE_A);
+ vpn.onUserAdded(RESTRICTED_PROFILE_A.id);
+
+ // Expect restricted user range to be added.
+ verify(mConnectivityManager).setRequireVpnForUids(true,
+ toRanges(restrictedUserRangeParcel));
+
+ // Mark as partial indicates that the user is removed, mUserManager.getAliveUsers() does not
+ // return the restricted user but it is still returned in mUserManager.getUserInfo().
+ RESTRICTED_PROFILE_A.partial = true;
+ // Remove the restricted user
+ vpn.onUserRemoved(RESTRICTED_PROFILE_A.id);
+ verify(mConnectivityManager).setRequireVpnForUids(false,
+ toRanges(restrictedUserRangeParcel));
+ // reset to avoid affecting other tests since RESTRICTED_PROFILE_A is static.
+ RESTRICTED_PROFILE_A.partial = false;
+ }
+
+ @Test
+ public void testOnUserAddedAndRemoved_restrictedUserAlwaysOn() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+
+ // setAlwaysOnPackage() calls setRequireVpnForUids()
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true /* lockdown */, null /* lockdownAllowlist */));
+ final List<Integer> excludedUids = List.of(PKG_UIDS[0]);
+ final List<Range<Integer>> primaryRanges =
+ makeVpnUidRange(PRIMARY_USER.id, excludedUids);
+ verify(mConnectivityManager).setRequireVpnForUids(true, primaryRanges);
+
+ // Add the restricted user
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ setMockedUsers(PRIMARY_USER, RESTRICTED_PROFILE_A);
+ vpn.onUserAdded(RESTRICTED_PROFILE_A.id);
+
+ final List<Range<Integer>> restrictedRanges =
+ makeVpnUidRange(RESTRICTED_PROFILE_A.id, excludedUids);
+ // Expect restricted user range to be added.
+ verify(mConnectivityManager).setRequireVpnForUids(true, restrictedRanges);
+
+ // Mark as partial indicates that the user is removed, mUserManager.getAliveUsers() does not
+ // return the restricted user but it is still returned in mUserManager.getUserInfo().
+ RESTRICTED_PROFILE_A.partial = true;
+ // Remove the restricted user
+ vpn.onUserRemoved(RESTRICTED_PROFILE_A.id);
+ verify(mConnectivityManager).setRequireVpnForUids(false, restrictedRanges);
+
+ // reset to avoid affecting other tests since RESTRICTED_PROFILE_A is static.
+ RESTRICTED_PROFILE_A.partial = false;
+ }
+
+ @Test
+ public void testPrepare_throwSecurityExceptionWhenGivenPackageDoesNotBelongToTheCaller()
+ throws Exception {
+ mTestDeps.mIgnoreCallingUidChecks = false;
+ final Vpn vpn = createVpn();
+ assertThrows(SecurityException.class,
+ () -> vpn.prepare("com.not.vpn.owner", null, VpnManager.TYPE_VPN_SERVICE));
+ assertThrows(SecurityException.class,
+ () -> vpn.prepare(null, "com.not.vpn.owner", VpnManager.TYPE_VPN_SERVICE));
+ assertThrows(SecurityException.class,
+ () -> vpn.prepare("com.not.vpn.owner1", "com.not.vpn.owner2",
+ VpnManager.TYPE_VPN_SERVICE));
+ }
+
+ @Test
+ public void testPrepare_bothOldPackageAndNewPackageAreNull() throws Exception {
+ final Vpn vpn = createVpn();
+ assertTrue(vpn.prepare(null, null, VpnManager.TYPE_VPN_SERVICE));
+
+ }
+
+ @Test
+ public void testPrepare_legacyVpnWithoutControlVpn()
+ throws Exception {
+ doThrow(new SecurityException("no CONTROL_VPN")).when(mContext)
+ .enforceCallingOrSelfPermission(eq(CONTROL_VPN), any());
+ final Vpn vpn = createVpn();
+ assertThrows(SecurityException.class,
+ () -> vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE));
+
+ // CONTROL_VPN can be held by the caller or another system server process - both are
+ // allowed. Just checking for `enforceCallingPermission` may not be sufficient.
+ verify(mContext, never()).enforceCallingPermission(eq(CONTROL_VPN), any());
+ }
+
+ @Test
+ public void testPrepare_legacyVpnWithControlVpn()
+ throws Exception {
+ doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CONTROL_VPN), any());
+ final Vpn vpn = createVpn();
+ assertTrue(vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE));
+
+ // CONTROL_VPN can be held by the caller or another system server process - both are
+ // allowed. Just checking for `enforceCallingPermission` may not be sufficient.
+ verify(mContext, never()).enforceCallingPermission(eq(CONTROL_VPN), any());
+ }
+
+ @Test
+ public void testIsAlwaysOnPackageSupported() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+
+ ApplicationInfo appInfo = new ApplicationInfo();
+ when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(PRIMARY_USER.id)))
+ .thenReturn(appInfo);
+
+ ServiceInfo svcInfo = new ServiceInfo();
+ ResolveInfo resInfo = new ResolveInfo();
+ resInfo.serviceInfo = svcInfo;
+ when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
+ eq(PRIMARY_USER.id)))
+ .thenReturn(Collections.singletonList(resInfo));
+
+ // null package name should return false
+ assertFalse(vpn.isAlwaysOnPackageSupported(null));
+
+ // Pre-N apps are not supported
+ appInfo.targetSdkVersion = VERSION_CODES.M;
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+
+ // N+ apps are supported by default
+ appInfo.targetSdkVersion = VERSION_CODES.N;
+ assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+
+ // Apps that opt out explicitly are not supported
+ appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
+ Bundle metaData = new Bundle();
+ metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
+ svcInfo.metaData = metaData;
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ }
+
+ @Test
+ public void testNotificationShownForAlwaysOnApp() throws Exception {
+ final UserHandle userHandle = UserHandle.of(PRIMARY_USER.id);
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ setMockedUsers(PRIMARY_USER);
+
+ final InOrder order = inOrder(mNotificationManager);
+
+ // Don't show a notification for regular disconnected states.
+ vpn.updateState(DetailedState.DISCONNECTED, TAG);
+ order.verify(mNotificationManager, atLeastOnce()).cancel(anyString(), anyInt());
+
+ // Start showing a notification for disconnected once always-on.
+ vpn.setAlwaysOnPackage(PKGS[0], false, null);
+ order.verify(mNotificationManager).notify(anyString(), anyInt(), any());
+
+ // Stop showing the notification once connected.
+ vpn.updateState(DetailedState.CONNECTED, TAG);
+ order.verify(mNotificationManager).cancel(anyString(), anyInt());
+
+ // Show the notification if we disconnect again.
+ vpn.updateState(DetailedState.DISCONNECTED, TAG);
+ order.verify(mNotificationManager).notify(anyString(), anyInt(), any());
+
+ // Notification should be cleared after unsetting always-on package.
+ vpn.setAlwaysOnPackage(null, false, null);
+ order.verify(mNotificationManager).cancel(anyString(), anyInt());
+ }
+
+ /**
+ * The profile name should NOT change between releases for backwards compatibility
+ *
+ * <p>If this is changed between releases, the {@link Vpn#getVpnProfilePrivileged()} method MUST
+ * be updated to ensure backward compatibility.
+ */
+ @Test
+ public void testGetProfileNameForPackage() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ setMockedUsers(PRIMARY_USER);
+
+ final String expected = Credentials.PLATFORM_VPN + PRIMARY_USER.id + "_" + TEST_VPN_PKG;
+ assertEquals(expected, vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ }
+
+ private Vpn createVpn(String... grantedOps) throws Exception {
+ return createVpn(PRIMARY_USER, grantedOps);
+ }
+
+ private Vpn createVpn(UserInfo user, String... grantedOps) throws Exception {
+ final Vpn vpn = createVpn(user.id);
+ setMockedUsers(user);
+
+ for (final String opStr : grantedOps) {
+ when(mAppOps.noteOpNoThrow(opStr, Process.myUid(), TEST_VPN_PKG,
+ null /* attributionTag */, null /* message */))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+
+ return vpn;
+ }
+
+ private void checkProvisionVpnProfile(Vpn vpn, boolean expectedResult, String... checkedOps) {
+ assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile));
+
+ // The profile should always be stored, whether or not consent has been previously granted.
+ verify(mVpnProfileStore)
+ .put(
+ eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)),
+ eq(mVpnProfile.encode()));
+
+ for (final String checkedOpStr : checkedOps) {
+ verify(mAppOps).noteOpNoThrow(checkedOpStr, Process.myUid(), TEST_VPN_PKG,
+ null /* attributionTag */, null /* message */);
+ }
+ }
+
+ @Test
+ public void testProvisionVpnProfileNoIpsecTunnels() throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+ .thenReturn(false);
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ checkProvisionVpnProfile(
+ vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ fail("Expected exception due to missing feature");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ private String startVpnForVerifyAppExclusionList(Vpn vpn) throws Exception {
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ when(mVpnProfileStore.get(PRIMARY_USER_APP_EXCLUDE_KEY))
+ .thenReturn(HexDump.hexStringToByteArray(PKGS_BYTES));
+ final String sessionKey = vpn.startVpnProfile(TEST_VPN_PKG);
+ final Set<Range<Integer>> uidRanges = vpn.createUserAndRestrictedProfilesRanges(
+ PRIMARY_USER.id, null /* allowedApplications */, Arrays.asList(PKGS));
+ verify(mConnectivityManager).setVpnDefaultForUids(eq(sessionKey), eq(uidRanges));
+ clearInvocations(mConnectivityManager);
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ vpn.mNetworkAgent = mMockNetworkAgent;
+
+ return sessionKey;
+ }
+
+ private Vpn prepareVpnForVerifyAppExclusionList() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ startVpnForVerifyAppExclusionList(vpn);
+
+ return vpn;
+ }
+
+ @Test
+ public void testSetAndGetAppExclusionList() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ final String sessionKey = startVpnForVerifyAppExclusionList(vpn);
+ verify(mVpnProfileStore, never()).put(eq(PRIMARY_USER_APP_EXCLUDE_KEY), any());
+ vpn.setAppExclusionList(TEST_VPN_PKG, Arrays.asList(PKGS));
+ verify(mVpnProfileStore)
+ .put(eq(PRIMARY_USER_APP_EXCLUDE_KEY),
+ eq(HexDump.hexStringToByteArray(PKGS_BYTES)));
+ final Set<Range<Integer>> uidRanges = vpn.createUserAndRestrictedProfilesRanges(
+ PRIMARY_USER.id, null /* allowedApplications */, Arrays.asList(PKGS));
+ verify(mConnectivityManager).setVpnDefaultForUids(eq(sessionKey), eq(uidRanges));
+ assertEquals(uidRanges, vpn.mNetworkCapabilities.getUids());
+ assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
+ }
+
+ @Test
+ public void testRefreshPlatformVpnAppExclusionList_updatesExcludedUids() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ final String sessionKey = startVpnForVerifyAppExclusionList(vpn);
+ vpn.setAppExclusionList(TEST_VPN_PKG, Arrays.asList(PKGS));
+ final Set<Range<Integer>> uidRanges = vpn.createUserAndRestrictedProfilesRanges(
+ PRIMARY_USER.id, null /* allowedApplications */, Arrays.asList(PKGS));
+ verify(mConnectivityManager).setVpnDefaultForUids(eq(sessionKey), eq(uidRanges));
+ verify(mMockNetworkAgent).doSendNetworkCapabilities(any());
+ assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
+
+ reset(mMockNetworkAgent);
+
+ // Remove one of the package
+ List<Integer> newExcludedUids = toList(PKG_UIDS);
+ newExcludedUids.remove((Integer) PKG_UIDS[0]);
+ Set<Range<Integer>> newUidRanges = makeVpnUidRangeSet(PRIMARY_USER.id, newExcludedUids);
+ sPackages.remove(PKGS[0]);
+ vpn.refreshPlatformVpnAppExclusionList();
+
+ // List in keystore is not changed, but UID for the removed packages is no longer exempted.
+ assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
+ assertEquals(newUidRanges, vpn.mNetworkCapabilities.getUids());
+ ArgumentCaptor<NetworkCapabilities> ncCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
+ verify(mMockNetworkAgent).doSendNetworkCapabilities(ncCaptor.capture());
+ assertEquals(newUidRanges, ncCaptor.getValue().getUids());
+ verify(mConnectivityManager).setVpnDefaultForUids(eq(sessionKey), eq(newUidRanges));
+
+ reset(mMockNetworkAgent);
+
+ // Add the package back
+ newExcludedUids.add(PKG_UIDS[0]);
+ newUidRanges = makeVpnUidRangeSet(PRIMARY_USER.id, newExcludedUids);
+ sPackages.put(PKGS[0], PKG_UIDS[0]);
+ vpn.refreshPlatformVpnAppExclusionList();
+
+ // List in keystore is not changed and the uid list should be updated in the net cap.
+ assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
+ assertEquals(newUidRanges, vpn.mNetworkCapabilities.getUids());
+ verify(mMockNetworkAgent).doSendNetworkCapabilities(ncCaptor.capture());
+ assertEquals(newUidRanges, ncCaptor.getValue().getUids());
+
+ // The uidRange is the same as the original setAppExclusionList so this is the second call
+ verify(mConnectivityManager, times(2))
+ .setVpnDefaultForUids(eq(sessionKey), eq(newUidRanges));
+ }
+
+ private List<Range<Integer>> makeVpnUidRange(int userId, List<Integer> excludedAppIdList) {
+ final SortedSet<Integer> list = new TreeSet<>();
+
+ final int userBase = userId * UserHandle.PER_USER_RANGE;
+ for (int appId : excludedAppIdList) {
+ final int uid = UserHandle.getUid(userId, appId);
+ list.add(uid);
+ if (Process.isApplicationUid(uid)) {
+ list.add(Process.toSdkSandboxUid(uid)); // Add Sdk Sandbox UID
+ }
+ }
+
+ final int minUid = userBase;
+ final int maxUid = userBase + UserHandle.PER_USER_RANGE - 1;
+ final List<Range<Integer>> ranges = new ArrayList<>();
+
+ // Iterate the list to create the ranges between each uid.
+ int start = minUid;
+ for (int uid : list) {
+ if (uid == start) {
+ start++;
+ } else {
+ ranges.add(new Range<>(start, uid - 1));
+ start = uid + 1;
+ }
+ }
+
+ // Create the range between last uid and max uid.
+ if (start <= maxUid) {
+ ranges.add(new Range<>(start, maxUid));
+ }
+
+ return ranges;
+ }
+
+ private Set<Range<Integer>> makeVpnUidRangeSet(int userId, List<Integer> excludedAppIdList) {
+ return new ArraySet<>(makeVpnUidRange(userId, excludedAppIdList));
+ }
+
+ @Test
+ public void testSetAndGetAppExclusionListRestrictedUser() throws Exception {
+ final Vpn vpn = prepareVpnForVerifyAppExclusionList();
+
+ // Mock it to restricted profile
+ when(mUserManager.getUserInfo(anyInt())).thenReturn(RESTRICTED_PROFILE_A);
+
+ // Restricted users cannot configure VPNs
+ assertThrows(SecurityException.class,
+ () -> vpn.setAppExclusionList(TEST_VPN_PKG, new ArrayList<>()));
+
+ assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
+ }
+
+ @Test
+ public void testProvisionVpnProfilePreconsented() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ checkProvisionVpnProfile(
+ vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileNotPreconsented() throws Exception {
+ final Vpn vpn = createVpn();
+
+ // Expect that both the ACTIVATE_VPN and ACTIVATE_PLATFORM_VPN were tried, but the caller
+ // had neither.
+ checkProvisionVpnProfile(vpn, false /* expectedResult */,
+ AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN, AppOpsManager.OPSTR_ACTIVATE_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileVpnServicePreconsented() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_VPN);
+
+ checkProvisionVpnProfile(vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileTooLarge() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ final VpnProfile bigProfile = new VpnProfile("");
+ bigProfile.name = new String(new byte[Vpn.MAX_VPN_PROFILE_SIZE_BYTES + 1]);
+
+ try {
+ vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile);
+ fail("Expected IAE due to profile size");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testProvisionVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpn(
+ RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testDeleteVpnProfile() throws Exception {
+ final Vpn vpn = createVpn();
+
+ vpn.deleteVpnProfile(TEST_VPN_PKG);
+
+ verify(mVpnProfileStore)
+ .remove(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ }
+
+ @Test
+ public void testDeleteVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpn(
+ RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.deleteVpnProfile(TEST_VPN_PKG);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testGetVpnProfilePrivileged() throws Exception {
+ final Vpn vpn = createVpn();
+
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(new VpnProfile("").encode());
+
+ vpn.getVpnProfilePrivileged(TEST_VPN_PKG);
+
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ }
+
+ private void verifyPlatformVpnIsActivated(String packageName) {
+ verify(mAppOps).noteOpNoThrow(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(packageName),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ verify(mAppOps).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(packageName),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ }
+
+ private void verifyPlatformVpnIsDeactivated(String packageName) {
+ // Add a small delay to double confirm that finishOp is only called once.
+ verify(mAppOps, after(100)).finishOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(packageName),
+ eq(null) /* attributionTag */);
+ }
+
+ @Test
+ public void testStartVpnProfile() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ vpn.startVpnProfile(TEST_VPN_PKG);
+
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ }
+
+ @Test
+ public void testStartVpnProfileVpnServicePreconsented() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_VPN);
+
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ vpn.startVpnProfile(TEST_VPN_PKG);
+
+ // Verify that the ACTIVATE_VPN appop was checked, but no error was thrown.
+ verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(),
+ TEST_VPN_PKG, null /* attributionTag */, null /* message */);
+ }
+
+ @Test
+ public void testStartVpnProfileNotConsented() throws Exception {
+ final Vpn vpn = createVpn();
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ fail("Expected failure due to no user consent");
+ } catch (SecurityException expected) {
+ }
+
+ // Verify both appops were checked.
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(),
+ TEST_VPN_PKG, null /* attributionTag */, null /* message */);
+
+ // Keystore should never have been accessed.
+ verify(mVpnProfileStore, never()).get(any());
+ }
+
+ @Test
+ public void testStartVpnProfileMissingProfile() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null);
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ fail("Expected failure due to missing profile");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ verify(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ }
+
+ @Test
+ public void testStartVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn = createVpn(RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testStopVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn = createVpn(RESTRICTED_PROFILE_A, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testStartOpAndFinishOpWillBeCalledWhenPlatformVpnIsOnAndOff() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ // Add a small delay to make sure that startOp is only called once.
+ verify(mAppOps, after(100).times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Check that the startOp is not called with OPSTR_ESTABLISH_VPN_SERVICE.
+ verify(mAppOps, never()).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
+ }
+
+ @Test
+ public void testStartOpWithSeamlessHandover() throws Exception {
+ // Create with SYSTEM_USER so that establish() will match the user ID when checking
+ // against Binder.getCallerUid
+ final Vpn vpn = createVpn(SYSTEM_USER, AppOpsManager.OPSTR_ACTIVATE_VPN);
+ assertTrue(vpn.prepare(TEST_VPN_PKG, null, VpnManager.TYPE_VPN_SERVICE));
+ final VpnConfig config = new VpnConfig();
+ config.user = "VpnTest";
+ config.addresses.add(new LinkAddress("192.0.2.2/32"));
+ config.mtu = 1450;
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.permission = BIND_VPN_SERVICE;
+ resolveInfo.serviceInfo = serviceInfo;
+ when(mPackageManager.resolveService(any(), anyInt())).thenReturn(resolveInfo);
+ when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+ vpn.establish(config);
+ verify(mAppOps, times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Call establish() twice with the same config, it should match seamless handover case and
+ // startOp() shouldn't be called again.
+ vpn.establish(config);
+ verify(mAppOps, times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ }
+
+ private void verifyVpnManagerEvent(String sessionKey, String category, int errorClass,
+ int errorCode, String[] packageName, @NonNull VpnProfileState... profileState) {
+ final Context userContext =
+ mContext.createContextAsUser(UserHandle.of(PRIMARY_USER.id), 0 /* flags */);
+ final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ final int verifyTimes = profileState.length;
+ verify(userContext, timeout(TEST_TIMEOUT_MS).times(verifyTimes))
+ .startService(intentArgumentCaptor.capture());
+
+ for (int i = 0; i < verifyTimes; i++) {
+ final Intent intent = intentArgumentCaptor.getAllValues().get(i);
+ assertEquals(packageName[i], intent.getPackage());
+ assertEquals(sessionKey, intent.getStringExtra(VpnManager.EXTRA_SESSION_KEY));
+ final Set<String> categories = intent.getCategories();
+ assertTrue(categories.contains(category));
+ assertEquals(1, categories.size());
+ assertEquals(errorClass,
+ intent.getIntExtra(VpnManager.EXTRA_ERROR_CLASS, -1 /* defaultValue */));
+ assertEquals(errorCode,
+ intent.getIntExtra(VpnManager.EXTRA_ERROR_CODE, -1 /* defaultValue */));
+ // CATEGORY_EVENT_DEACTIVATED_BY_USER & CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED won't
+ // send NetworkCapabilities & LinkProperties to VPN app.
+ // For ERROR_CODE_NETWORK_LOST, the NetworkCapabilities & LinkProperties of underlying
+ // network will be cleared. So the VPN app will receive null for those 2 extra values.
+ if (category.equals(VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER)
+ || category.equals(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED)
+ || errorCode == VpnManager.ERROR_CODE_NETWORK_LOST) {
+ assertNull(intent.getParcelableExtra(
+ VpnManager.EXTRA_UNDERLYING_NETWORK_CAPABILITIES));
+ assertNull(intent.getParcelableExtra(VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES));
+ } else {
+ assertNotNull(intent.getParcelableExtra(
+ VpnManager.EXTRA_UNDERLYING_NETWORK_CAPABILITIES));
+ assertNotNull(intent.getParcelableExtra(
+ VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES));
+ }
+
+ assertEquals(profileState[i], intent.getParcelableExtra(
+ VpnManager.EXTRA_VPN_PROFILE_STATE, VpnProfileState.class));
+ }
+ reset(userContext);
+ }
+
+ private void verifyDeactivatedByUser(String sessionKey, String[] packageName) {
+ // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
+ // errorCode won't be set.
+ verifyVpnManagerEvent(sessionKey, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
+ -1 /* errorClass */, -1 /* errorCode */, packageName,
+ // VPN NetworkAgnet does not switch to CONNECTED in the test, and the state is not
+ // important here. Verify that the state as it is, i.e. CONNECTING state.
+ new VpnProfileState(VpnProfileState.STATE_CONNECTING,
+ sessionKey, false /* alwaysOn */, false /* lockdown */));
+ }
+
+ private void verifyAlwaysOnStateChanged(String[] packageName, VpnProfileState... profileState) {
+ verifyVpnManagerEvent(null /* sessionKey */,
+ VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+ -1 /* errorCode */, packageName, profileState);
+ }
+
+ @Test
+ public void testVpnManagerEventForUserDeactivated() throws Exception {
+ // For security reasons, Vpn#prepare() will check that oldPackage and newPackage are either
+ // null or the package of the caller. This test will call Vpn#prepare() to pretend the old
+ // VPN is replaced by a new one. But only Settings can change to some other packages, and
+ // this is checked with CONTROL_VPN so simulate holding CONTROL_VPN in order to pass the
+ // security checks.
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ // Test the case that the user deactivates the vpn in vpn app.
+ final String sessionKey1 = vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
+ verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
+ reset(mDeviceIdleInternal);
+ verifyDeactivatedByUser(sessionKey1, new String[] {TEST_VPN_PKG});
+ reset(mAppOps);
+
+ // Test the case that the user chooses another vpn and the original one is replaced.
+ final String sessionKey2 = vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ vpn.prepare(TEST_VPN_PKG, "com.new.vpn" /* newPackage */, TYPE_VPN_PLATFORM);
+ verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
+ verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
+ reset(mDeviceIdleInternal);
+ verifyDeactivatedByUser(sessionKey2, new String[] {TEST_VPN_PKG});
+ }
+
+ @Test
+ public void testVpnManagerEventForAlwaysOnChanged() throws Exception {
+ // Calling setAlwaysOnPackage() needs to hold CONTROL_VPN.
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ // Enable VPN always-on for PKGS[1].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyPowerSaveTempWhitelistApp(PKGS[1]);
+ reset(mDeviceIdleInternal);
+ verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+
+ // Enable VPN lockdown for PKGS[1].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyPowerSaveTempWhitelistApp(PKGS[1]);
+ reset(mDeviceIdleInternal);
+ verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, true /* lockdown */));
+
+ // Disable VPN lockdown for PKGS[1].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyPowerSaveTempWhitelistApp(PKGS[1]);
+ reset(mDeviceIdleInternal);
+ verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+
+ // Disable VPN always-on.
+ assertTrue(vpn.setAlwaysOnPackage(null, false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyPowerSaveTempWhitelistApp(PKGS[1]);
+ reset(mDeviceIdleInternal);
+ verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, false /* alwaysOn */, false /* lockdown */));
+
+ // Enable VPN always-on for PKGS[1] again.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyPowerSaveTempWhitelistApp(PKGS[1]);
+ reset(mDeviceIdleInternal);
+ verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+
+ // Enable VPN always-on for PKGS[2].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[2], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyPowerSaveTempWhitelistApp(PKGS[2]);
+ reset(mDeviceIdleInternal);
+ // PKGS[1] is replaced with PKGS[2].
+ // Pass 2 VpnProfileState objects to verifyVpnManagerEvent(), the first one is sent to
+ // PKGS[1] to notify PKGS[1] that the VPN always-on is disabled, the second one is sent to
+ // PKGS[2] to notify PKGS[2] that the VPN always-on is enabled.
+ verifyAlwaysOnStateChanged(new String[] {PKGS[1], PKGS[2]},
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, false /* alwaysOn */, false /* lockdown */),
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+ }
+
+ @Test
+ public void testReconnectVpnManagerVpnWithAlwaysOnEnabled() throws Exception {
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+
+ // Enable VPN always-on for TEST_VPN_PKG.
+ assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, false /* lockdown */,
+ null /* lockdownAllowlist */));
+
+ // Reset to verify next startVpnProfile.
+ reset(mAppOps);
+
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+
+ // Reconnect the vpn with different package will cause exception.
+ assertThrows(SecurityException.class, () -> vpn.startVpnProfile(PKGS[0]));
+
+ // Reconnect the vpn again with the vpn always on package w/o exception.
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ }
+
+ @Test
+ public void testLockdown_enableDisableWhileConnected() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+ final InOrder order = inOrder(mTestDeps);
+ order.verify(mTestDeps, timeout(TIMEOUT_CROSSTHREAD_MS))
+ .newNetworkAgent(any(), any(), any(), any(), any(), any(),
+ argThat(config -> config.allowBypass), any(), any());
+
+ // Make VPN lockdown.
+ assertTrue(vpnSnapShot.vpn.setAlwaysOnPackage(TEST_VPN_PKG, true /* lockdown */,
+ null /* lockdownAllowlist */));
+
+ order.verify(mTestDeps, timeout(TIMEOUT_CROSSTHREAD_MS))
+ .newNetworkAgent(any(), any(), any(), any(), any(), any(),
+ argThat(config -> !config.allowBypass), any(), any());
+
+ // Disable lockdown.
+ assertTrue(vpnSnapShot.vpn.setAlwaysOnPackage(TEST_VPN_PKG, false /* lockdown */,
+ null /* lockdownAllowlist */));
+
+ order.verify(mTestDeps, timeout(TIMEOUT_CROSSTHREAD_MS))
+ .newNetworkAgent(any(), any(), any(), any(), any(), any(),
+ argThat(config -> config.allowBypass), any(), any());
+ }
+
+ @Test
+ public void testSetPackageAuthorizationVpnService() throws Exception {
+ final Vpn vpn = createVpn();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OPSTR_ACTIVATE_VPN),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationPlatformVpn() throws Exception {
+ final Vpn vpn = createVpn();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, TYPE_VPN_PLATFORM));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationRevokeAuthorization() throws Exception {
+ final Vpn vpn = createVpn();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OPSTR_ACTIVATE_VPN),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(UserHandle.getUid(PRIMARY_USER.id, Process.myUid())),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ }
+
+ private NetworkCallback triggerOnAvailableAndGetCallback() throws Exception {
+ return triggerOnAvailableAndGetCallback(new NetworkCapabilities.Builder().build());
+ }
+
+ private NetworkCallback triggerOnAvailableAndGetCallback(
+ @NonNull final NetworkCapabilities caps) throws Exception {
+ final ArgumentCaptor<NetworkCallback> networkCallbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
+ .registerSystemDefaultNetworkCallback(networkCallbackCaptor.capture(), any());
+
+ // onAvailable() will trigger onDefaultNetworkChanged(), so NetdUtils#setInterfaceUp will be
+ // invoked. Set the return value of INetd#interfaceGetCfg to prevent NullPointerException.
+ final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel();
+ config.flags = new String[] {IF_STATE_DOWN};
+ when(mNetd.interfaceGetCfg(anyString())).thenReturn(config);
+ final NetworkCallback cb = networkCallbackCaptor.getValue();
+ cb.onAvailable(TEST_NETWORK);
+ // Trigger onCapabilitiesChanged() and onLinkPropertiesChanged() so the test can verify that
+ // if NetworkCapabilities and LinkProperties of underlying network will be sent/cleared or
+ // not.
+ // See verifyVpnManagerEvent().
+ cb.onCapabilitiesChanged(TEST_NETWORK, caps);
+ cb.onLinkPropertiesChanged(TEST_NETWORK, new LinkProperties());
+ return cb;
+ }
+
+ private void verifyInterfaceSetCfgWithFlags(String flag) throws Exception {
+ // Add a timeout for waiting for interfaceSetCfg to be called.
+ verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetCfg(argThat(
+ config -> Arrays.asList(config.flags).contains(flag)));
+ }
+
+ private void doTestPlatformVpnWithException(IkeException exception,
+ String category, int errorType, int errorCode) throws Exception {
+ final ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ doReturn(new NetworkCapabilities()).when(mConnectivityManager)
+ .getRedactedNetworkCapabilitiesForPackage(any(), anyInt(), anyString());
+ doReturn(new LinkProperties()).when(mConnectivityManager)
+ .getRedactedLinkPropertiesForPackage(any(), anyInt(), anyString());
+
+ final String sessionKey = vpn.startVpnProfile(TEST_VPN_PKG);
+ final Set<Range<Integer>> uidRanges = rangeSet(PRIMARY_USER_RANGE);
+ // This is triggered by Ikev2VpnRunner constructor.
+ verify(mConnectivityManager, times(1)).setVpnDefaultForUids(eq(sessionKey), eq(uidRanges));
+ final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+
+ verifyInterfaceSetCfgWithFlags(IF_STATE_UP);
+
+ // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
+ // state
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
+ .createIkeSession(any(), any(), any(), any(), captor.capture(), any());
+ // This is triggered by Vpn#startOrMigrateIkeSession().
+ verify(mConnectivityManager, times(2)).setVpnDefaultForUids(eq(sessionKey), eq(uidRanges));
+ reset(mIkev2SessionCreator);
+ // For network lost case, the process should be triggered by calling onLost(), which is the
+ // same process with the real case.
+ if (errorCode == VpnManager.ERROR_CODE_NETWORK_LOST) {
+ cb.onLost(TEST_NETWORK);
+ verify(mExecutor, atLeastOnce()).schedule(any(Runnable.class), anyLong(), any());
+ } else {
+ final IkeSessionCallback ikeCb = captor.getValue();
+ mExecutor.execute(() -> ikeCb.onClosedWithException(exception));
+ }
+
+ verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
+ reset(mDeviceIdleInternal);
+ verifyVpnManagerEvent(sessionKey, category, errorType, errorCode,
+ // VPN NetworkAgnet does not switch to CONNECTED in the test, and the state is not
+ // important here. Verify that the state as it is, i.e. CONNECTING state.
+ new String[] {TEST_VPN_PKG}, new VpnProfileState(VpnProfileState.STATE_CONNECTING,
+ sessionKey, false /* alwaysOn */, false /* lockdown */));
+ if (errorType == VpnManager.ERROR_CLASS_NOT_RECOVERABLE) {
+ verify(mConnectivityManager).setVpnDefaultForUids(eq(sessionKey),
+ eq(Collections.EMPTY_LIST));
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
+ .unregisterNetworkCallback(eq(cb));
+ } else if (errorType == VpnManager.ERROR_CLASS_RECOVERABLE
+ // Vpn won't retry when there is no usable underlying network.
+ && errorCode != VpnManager.ERROR_CODE_NETWORK_LOST) {
+ int retryIndex = 0;
+ // First failure occurred above.
+ final IkeSessionCallback retryCb = verifyRetryAndGetNewIkeCb(retryIndex++);
+ // Trigger 2 more failures to let the retry delay increase to 5s.
+ mExecutor.execute(() -> retryCb.onClosedWithException(exception));
+ final IkeSessionCallback retryCb2 = verifyRetryAndGetNewIkeCb(retryIndex++);
+ mExecutor.execute(() -> retryCb2.onClosedWithException(exception));
+ final IkeSessionCallback retryCb3 = verifyRetryAndGetNewIkeCb(retryIndex++);
+
+ // setVpnDefaultForUids may be called again but the uidRanges should not change.
+ verify(mConnectivityManager, atLeast(2)).setVpnDefaultForUids(eq(sessionKey),
+ mUidRangesCaptor.capture());
+ final List<Collection<Range<Integer>>> capturedUidRanges =
+ mUidRangesCaptor.getAllValues();
+ for (int i = 2; i < capturedUidRanges.size(); i++) {
+ // Assert equals no order.
+ assertTrue(
+ "uid ranges should not be modified. Expected: " + uidRanges
+ + ", actual: " + capturedUidRanges.get(i),
+ capturedUidRanges.get(i).containsAll(uidRanges)
+ && capturedUidRanges.get(i).size() == uidRanges.size());
+ }
+
+ // A fourth failure will cause the retry delay to be greater than 5s.
+ mExecutor.execute(() -> retryCb3.onClosedWithException(exception));
+ verifyRetryAndGetNewIkeCb(retryIndex++);
+
+ // The VPN network preference will be cleared when the retry delay is greater than 5s.
+ verify(mConnectivityManager).setVpnDefaultForUids(eq(sessionKey),
+ eq(Collections.EMPTY_LIST));
+ }
+ }
+
+ private IkeSessionCallback verifyRetryAndGetNewIkeCb(int retryIndex) {
+ final ArgumentCaptor<IkeSessionCallback> ikeCbCaptor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+
+ // Verify retry is scheduled
+ final long expectedDelayMs = mTestDeps.getNextRetryDelayMs(retryIndex);
+ verify(mExecutor, timeout(TEST_TIMEOUT_MS)).schedule(any(Runnable.class),
+ eq(expectedDelayMs), eq(TimeUnit.MILLISECONDS));
+
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS + expectedDelayMs))
+ .createIkeSession(any(), any(), any(), any(), ikeCbCaptor.capture(), any());
+
+ // Forget the mIkev2SessionCreator#createIkeSession call and mExecutor#schedule call
+ // for the next retry verification
+ resetIkev2SessionCreator(mIkeSessionWrapper);
+
+ return ikeCbCaptor.getValue();
+ }
+
+ @Test
+ public void testStartPlatformVpnAuthenticationFailed() throws Exception {
+ final IkeProtocolException exception = mock(IkeProtocolException.class);
+ final int errorCode = IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
+ when(exception.getErrorType()).thenReturn(errorCode);
+ doTestPlatformVpnWithException(exception,
+ VpnManager.CATEGORY_EVENT_IKE_ERROR, VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
+ errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithRecoverableError() throws Exception {
+ final IkeProtocolException exception = mock(IkeProtocolException.class);
+ final int errorCode = IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
+ when(exception.getErrorType()).thenReturn(errorCode);
+ doTestPlatformVpnWithException(exception,
+ VpnManager.CATEGORY_EVENT_IKE_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE, errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithUnknownHostException() throws Exception {
+ final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+ final UnknownHostException unknownHostException = new UnknownHostException();
+ final int errorCode = VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST;
+ when(exception.getCause()).thenReturn(unknownHostException);
+ doTestPlatformVpnWithException(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithIkeTimeoutException() throws Exception {
+ final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+ final IkeTimeoutException ikeTimeoutException =
+ new IkeTimeoutException("IkeTimeoutException");
+ final int errorCode = VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT;
+ when(exception.getCause()).thenReturn(ikeTimeoutException);
+ doTestPlatformVpnWithException(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithIkeNetworkLostException() throws Exception {
+ final IkeNetworkLostException exception = new IkeNetworkLostException(
+ new Network(100));
+ doTestPlatformVpnWithException(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ VpnManager.ERROR_CODE_NETWORK_LOST);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithIOException() throws Exception {
+ final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+ final IOException ioException = new IOException();
+ final int errorCode = VpnManager.ERROR_CODE_NETWORK_IO;
+ when(exception.getCause()).thenReturn(ioException);
+ doTestPlatformVpnWithException(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception {
+ when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any()))
+ .thenThrow(new IllegalArgumentException());
+ final Vpn vpn = startLegacyVpn(createVpn(PRIMARY_USER.id), mVpnProfile);
+ final NetworkCallback cb = triggerOnAvailableAndGetCallback();
+
+ verifyInterfaceSetCfgWithFlags(IF_STATE_UP);
+
+ // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
+ // state
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
+ assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state);
+ }
+
+ @Test
+ public void testVpnManagerEventWillNotBeSentToSettingsVpn() throws Exception {
+ startLegacyVpn(createVpn(PRIMARY_USER.id), mVpnProfile);
+ triggerOnAvailableAndGetCallback();
+
+ verifyInterfaceSetCfgWithFlags(IF_STATE_UP);
+
+ final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+ final IkeTimeoutException ikeTimeoutException =
+ new IkeTimeoutException("IkeTimeoutException");
+ when(exception.getCause()).thenReturn(ikeTimeoutException);
+
+ final ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
+ .createIkeSession(any(), any(), any(), any(), captor.capture(), any());
+ final IkeSessionCallback ikeCb = captor.getValue();
+ ikeCb.onClosedWithException(exception);
+
+ final Context userContext =
+ mContext.createContextAsUser(UserHandle.of(PRIMARY_USER.id), 0 /* flags */);
+ verify(userContext, never()).startService(any());
+ }
+
+ private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
+ assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null));
+
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mAppOps).setMode(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(PRIMARY_USER.id));
+ verify(mSystemServices).settingsSecurePutIntForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0),
+ eq(PRIMARY_USER.id));
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(PRIMARY_USER.id));
+ }
+
+ @Test
+ public void testSetAndStartAlwaysOnVpn() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ setMockedUsers(PRIMARY_USER);
+
+ // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+ final int uid = Process.myUid() + 1;
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(uid);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ setAndVerifyAlwaysOnPackage(vpn, uid, false);
+ assertTrue(vpn.startAlwaysOnVpn());
+
+ // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+ // a subsequent CL.
+ }
+
+ private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
+ setMockedUsers(PRIMARY_USER);
+ vpn.startLegacyVpn(vpnProfile);
+ return vpn;
+ }
+
+ private IkeSessionConnectionInfo createIkeConnectInfo() {
+ return new IkeSessionConnectionInfo(TEST_VPN_CLIENT_IP, TEST_VPN_SERVER_IP, TEST_NETWORK);
+ }
+
+ private IkeSessionConnectionInfo createIkeConnectInfo_2() {
+ return new IkeSessionConnectionInfo(
+ TEST_VPN_CLIENT_IP_2, TEST_VPN_SERVER_IP_2, TEST_NETWORK_2);
+ }
+
+ private IkeSessionConfiguration createIkeConfig(
+ IkeSessionConnectionInfo ikeConnectInfo, boolean isMobikeEnabled) {
+ final IkeSessionConfiguration.Builder builder =
+ new IkeSessionConfiguration.Builder(ikeConnectInfo);
+
+ if (isMobikeEnabled) {
+ builder.addIkeExtension(EXTENSION_TYPE_MOBIKE);
+ }
+
+ return builder.build();
+ }
+
+ private ChildSessionConfiguration createChildConfig() {
+ return new ChildSessionConfiguration.Builder(
+ Arrays.asList(IN_TS, IN_TS6), Arrays.asList(OUT_TS, OUT_TS6))
+ .addInternalAddress(new LinkAddress(TEST_VPN_INTERNAL_IP, IP4_PREFIX_LEN))
+ .addInternalAddress(new LinkAddress(TEST_VPN_INTERNAL_IP6, IP6_PREFIX_LEN))
+ .addInternalDnsServer(TEST_VPN_INTERNAL_DNS)
+ .addInternalDnsServer(TEST_VPN_INTERNAL_DNS6)
+ .build();
+ }
+
+ private IpSecTransform createIpSecTransform() {
+ return new IpSecTransform(mContext, new IpSecConfig());
+ }
+
+ private void verifyApplyTunnelModeTransforms(int expectedTimes) throws Exception {
+ verify(mIpSecService, times(expectedTimes)).applyTunnelModeTransform(
+ eq(TEST_TUNNEL_RESOURCE_ID), eq(IpSecManager.DIRECTION_IN),
+ anyInt(), anyString());
+ verify(mIpSecService, times(expectedTimes)).applyTunnelModeTransform(
+ eq(TEST_TUNNEL_RESOURCE_ID), eq(IpSecManager.DIRECTION_OUT),
+ anyInt(), anyString());
+ }
+
+ private Pair<IkeSessionCallback, ChildSessionCallback> verifyCreateIkeAndCaptureCbs()
+ throws Exception {
+ final ArgumentCaptor<IkeSessionCallback> ikeCbCaptor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ final ArgumentCaptor<ChildSessionCallback> childCbCaptor =
+ ArgumentCaptor.forClass(ChildSessionCallback.class);
+
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS)).createIkeSession(
+ any(), any(), any(), any(), ikeCbCaptor.capture(), childCbCaptor.capture());
+
+ return new Pair<>(ikeCbCaptor.getValue(), childCbCaptor.getValue());
+ }
+
+ private static class PlatformVpnSnapshot {
+ public final Vpn vpn;
+ public final NetworkCallback nwCb;
+ public final IkeSessionCallback ikeCb;
+ public final ChildSessionCallback childCb;
+
+ PlatformVpnSnapshot(Vpn vpn, NetworkCallback nwCb,
+ IkeSessionCallback ikeCb, ChildSessionCallback childCb) {
+ this.vpn = vpn;
+ this.nwCb = nwCb;
+ this.ikeCb = ikeCb;
+ this.childCb = childCb;
+ }
+ }
+
+ private PlatformVpnSnapshot verifySetupPlatformVpn(IkeSessionConfiguration ikeConfig)
+ throws Exception {
+ return verifySetupPlatformVpn(ikeConfig, true);
+ }
+
+ private PlatformVpnSnapshot verifySetupPlatformVpn(
+ IkeSessionConfiguration ikeConfig, boolean mtuSupportsIpv6) throws Exception {
+ return verifySetupPlatformVpn(mVpnProfile, ikeConfig, mtuSupportsIpv6);
+ }
+
+ private PlatformVpnSnapshot verifySetupPlatformVpn(VpnProfile vpnProfile,
+ IkeSessionConfiguration ikeConfig, boolean mtuSupportsIpv6) throws Exception {
+ return verifySetupPlatformVpn(vpnProfile, ikeConfig,
+ new NetworkCapabilities.Builder().build() /* underlying network caps */,
+ mtuSupportsIpv6, false /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ private PlatformVpnSnapshot verifySetupPlatformVpn(VpnProfile vpnProfile,
+ IkeSessionConfiguration ikeConfig,
+ @NonNull final NetworkCapabilities underlyingNetworkCaps,
+ boolean mtuSupportsIpv6,
+ boolean areLongLivedTcpConnectionsExpensive) throws Exception {
+ if (!mtuSupportsIpv6) {
+ doReturn(IPV6_MIN_MTU - 1).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(),
+ anyBoolean());
+ }
+
+ doReturn(mMockNetworkAgent).when(mTestDeps)
+ .newNetworkAgent(
+ any(), any(), anyString(), any(), any(), any(), any(), any(), any());
+ doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+
+ final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(vpnProfile.encode());
+
+ final String sessionKey = vpn.startVpnProfile(TEST_VPN_PKG);
+ final Set<Range<Integer>> uidRanges = Collections.singleton(PRIMARY_USER_RANGE);
+ verify(mConnectivityManager).setVpnDefaultForUids(eq(sessionKey), eq(uidRanges));
+ final NetworkCallback nwCb = triggerOnAvailableAndGetCallback(underlyingNetworkCaps);
+ // There are 4 interactions with the executor.
+ // - Network available
+ // - LP change
+ // - NC change
+ // - schedule() calls in scheduleStartIkeSession()
+ // The first 3 calls are triggered from Executor.execute(). The execute() will also call to
+ // schedule() with 0 delay. Verify the exact interaction here so that it won't cause flakes
+ // in the follow-up flow.
+ verify(mExecutor, timeout(TEST_TIMEOUT_MS).times(4))
+ .schedule(any(Runnable.class), anyLong(), any());
+ reset(mExecutor);
+
+ // Mock the setup procedure by firing callbacks
+ final Pair<IkeSessionCallback, ChildSessionCallback> cbPair =
+ verifyCreateIkeAndCaptureCbs();
+ final IkeSessionCallback ikeCb = cbPair.first;
+ final ChildSessionCallback childCb = cbPair.second;
+
+ ikeCb.onOpened(ikeConfig);
+ childCb.onIpSecTransformCreated(createIpSecTransform(), IpSecManager.DIRECTION_IN);
+ childCb.onIpSecTransformCreated(createIpSecTransform(), IpSecManager.DIRECTION_OUT);
+ childCb.onOpened(createChildConfig());
+
+ // Verification VPN setup
+ verifyApplyTunnelModeTransforms(1);
+
+ ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+ ArgumentCaptor<NetworkCapabilities> ncCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
+ ArgumentCaptor<NetworkAgentConfig> nacCaptor =
+ ArgumentCaptor.forClass(NetworkAgentConfig.class);
+ verify(mTestDeps).newNetworkAgent(
+ any(), any(), anyString(), ncCaptor.capture(), lpCaptor.capture(),
+ any(), nacCaptor.capture(), any(), any());
+ verify(mIkeSessionWrapper).setUnderpinnedNetwork(TEST_NETWORK);
+ // Check LinkProperties
+ final LinkProperties lp = lpCaptor.getValue();
+ final List<RouteInfo> expectedRoutes =
+ new ArrayList<>(
+ Arrays.asList(
+ new RouteInfo(
+ new IpPrefix(Inet4Address.ANY, 0),
+ null /* gateway */,
+ TEST_IFACE_NAME,
+ RouteInfo.RTN_UNICAST)));
+ final List<LinkAddress> expectedAddresses =
+ new ArrayList<>(
+ Arrays.asList(new LinkAddress(TEST_VPN_INTERNAL_IP, IP4_PREFIX_LEN)));
+ final List<InetAddress> expectedDns = new ArrayList<>(Arrays.asList(TEST_VPN_INTERNAL_DNS));
+
+ if (mtuSupportsIpv6) {
+ expectedRoutes.add(
+ new RouteInfo(
+ new IpPrefix(Inet6Address.ANY, 0),
+ null /* gateway */,
+ TEST_IFACE_NAME,
+ RouteInfo.RTN_UNICAST));
+ expectedAddresses.add(new LinkAddress(TEST_VPN_INTERNAL_IP6, IP6_PREFIX_LEN));
+ expectedDns.add(TEST_VPN_INTERNAL_DNS6);
+ } else {
+ expectedRoutes.add(
+ new RouteInfo(
+ new IpPrefix(Inet6Address.ANY, 0),
+ null /* gateway */,
+ TEST_IFACE_NAME,
+ RTN_UNREACHABLE));
+ }
+
+ assertEquals(expectedRoutes, lp.getRoutes());
+ assertEquals(expectedAddresses, lp.getLinkAddresses());
+ assertEquals(expectedDns, lp.getDnsServers());
+
+ // Check NetworkCapabilities
+ assertEquals(Arrays.asList(TEST_NETWORK), ncCaptor.getValue().getUnderlyingNetworks());
+
+ // Check if allowBypass is set or not.
+ assertTrue(nacCaptor.getValue().isBypassableVpn());
+ // Check if extra info for VPN is set.
+ assertTrue(nacCaptor.getValue().getLegacyExtraInfo().contains(TEST_VPN_PKG));
+ final VpnTransportInfo info = (VpnTransportInfo) ncCaptor.getValue().getTransportInfo();
+ assertTrue(info.isBypassable());
+ assertEquals(areLongLivedTcpConnectionsExpensive,
+ info.areLongLivedTcpConnectionsExpensive());
+ return new PlatformVpnSnapshot(vpn, nwCb, ikeCb, childCb);
+ }
+
+ @Test
+ public void testStartPlatformVpn() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ verify(mConnectivityManager).setVpnDefaultForUids(anyString(), eq(Collections.EMPTY_LIST));
+ }
+
+ @Test
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AutoTimerNoTimer() throws Exception {
+ doTestMigrateIkeSession_FromIkeTunnConnParams(
+ false /* isAutomaticIpVersionSelectionEnabled */,
+ true /* isAutomaticNattKeepaliveTimerEnabled */,
+ TEST_KEEPALIVE_TIMEOUT_UNSET /* keepaliveInProfile */,
+ ESP_IP_VERSION_AUTO /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_AUTO /* encapTypeInProfile */);
+ }
+
+ @Test
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AutoTimerTimerSet() throws Exception {
+ doTestMigrateIkeSession_FromIkeTunnConnParams(
+ false /* isAutomaticIpVersionSelectionEnabled */,
+ true /* isAutomaticNattKeepaliveTimerEnabled */,
+ TEST_KEEPALIVE_TIMER /* keepaliveInProfile */,
+ ESP_IP_VERSION_AUTO /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_AUTO /* encapTypeInProfile */);
+ }
+
+ @Test
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AutoIp() throws Exception {
+ doTestMigrateIkeSession_FromIkeTunnConnParams(
+ true /* isAutomaticIpVersionSelectionEnabled */,
+ false /* isAutomaticNattKeepaliveTimerEnabled */,
+ TEST_KEEPALIVE_TIMEOUT_UNSET /* keepaliveInProfile */,
+ ESP_IP_VERSION_AUTO /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_AUTO /* encapTypeInProfile */);
+ }
+
+ @Test
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AssignedIpProtocol() throws Exception {
+ doTestMigrateIkeSession_FromIkeTunnConnParams(
+ false /* isAutomaticIpVersionSelectionEnabled */,
+ false /* isAutomaticNattKeepaliveTimerEnabled */,
+ TEST_KEEPALIVE_TIMEOUT_UNSET /* keepaliveInProfile */,
+ ESP_IP_VERSION_IPV4 /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_UDP /* encapTypeInProfile */);
+ }
+
+ @Test
+ public void testMigrateIkeSession_FromNotIkeTunnConnParams_AutoTimer() throws Exception {
+ doTestMigrateIkeSession_FromNotIkeTunnConnParams(
+ false /* isAutomaticIpVersionSelectionEnabled */,
+ true /* isAutomaticNattKeepaliveTimerEnabled */);
+ }
+
+ @Test
+ public void testMigrateIkeSession_FromNotIkeTunnConnParams_AutoIp() throws Exception {
+ doTestMigrateIkeSession_FromNotIkeTunnConnParams(
+ true /* isAutomaticIpVersionSelectionEnabled */,
+ false /* isAutomaticNattKeepaliveTimerEnabled */);
+ }
+
+ private void doTestMigrateIkeSession_FromNotIkeTunnConnParams(
+ boolean isAutomaticIpVersionSelectionEnabled,
+ boolean isAutomaticNattKeepaliveTimerEnabled) throws Exception {
+ final Ikev2VpnProfile ikeProfile =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY)
+ .setAuthPsk(TEST_VPN_PSK)
+ .setBypassable(true /* isBypassable */)
+ .setAutomaticNattKeepaliveTimerEnabled(isAutomaticNattKeepaliveTimerEnabled)
+ .setAutomaticIpVersionSelectionEnabled(isAutomaticIpVersionSelectionEnabled)
+ .build();
+
+ final int expectedKeepalive = isAutomaticNattKeepaliveTimerEnabled
+ ? AUTOMATIC_KEEPALIVE_DELAY_SECONDS
+ : DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
+ doTestMigrateIkeSession(ikeProfile.toVpnProfile(),
+ expectedKeepalive,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ new NetworkCapabilities.Builder().build());
+ }
+
+ private Ikev2VpnProfile makeIkeV2VpnProfile(
+ boolean isAutomaticIpVersionSelectionEnabled,
+ boolean isAutomaticNattKeepaliveTimerEnabled,
+ int keepaliveInProfile,
+ int ipVersionInProfile,
+ int encapTypeInProfile) {
+ // TODO: Update helper function in IkeSessionTestUtils to support building IkeSessionParams
+ // with IP version and encap type when mainline-prod branch support these two APIs.
+ final IkeSessionParams params = getTestIkeSessionParams(true /* testIpv6 */,
+ new IkeFqdnIdentification(TEST_IDENTITY), keepaliveInProfile);
+ final IkeSessionParams ikeSessionParams = new IkeSessionParams.Builder(params)
+ .setIpVersion(ipVersionInProfile)
+ .setEncapType(encapTypeInProfile)
+ .build();
+
+ final IkeTunnelConnectionParams tunnelParams =
+ new IkeTunnelConnectionParams(ikeSessionParams, CHILD_PARAMS);
+ return new Ikev2VpnProfile.Builder(tunnelParams)
+ .setBypassable(true)
+ .setAutomaticNattKeepaliveTimerEnabled(isAutomaticNattKeepaliveTimerEnabled)
+ .setAutomaticIpVersionSelectionEnabled(isAutomaticIpVersionSelectionEnabled)
+ .build();
+ }
+
+ private void doTestMigrateIkeSession_FromIkeTunnConnParams(
+ boolean isAutomaticIpVersionSelectionEnabled,
+ boolean isAutomaticNattKeepaliveTimerEnabled,
+ int keepaliveInProfile,
+ int ipVersionInProfile,
+ int encapTypeInProfile) throws Exception {
+ doTestMigrateIkeSession_FromIkeTunnConnParams(isAutomaticIpVersionSelectionEnabled,
+ isAutomaticNattKeepaliveTimerEnabled, keepaliveInProfile, ipVersionInProfile,
+ encapTypeInProfile, new NetworkCapabilities.Builder().build());
+ }
+
+ private void doTestMigrateIkeSession_FromIkeTunnConnParams(
+ boolean isAutomaticIpVersionSelectionEnabled,
+ boolean isAutomaticNattKeepaliveTimerEnabled,
+ int keepaliveInProfile,
+ int ipVersionInProfile,
+ int encapTypeInProfile,
+ @NonNull final NetworkCapabilities nc) throws Exception {
+ final Ikev2VpnProfile ikeProfile = makeIkeV2VpnProfile(
+ isAutomaticIpVersionSelectionEnabled,
+ isAutomaticNattKeepaliveTimerEnabled,
+ keepaliveInProfile,
+ ipVersionInProfile,
+ encapTypeInProfile);
+
+ final IkeSessionParams ikeSessionParams =
+ ikeProfile.getIkeTunnelConnectionParams().getIkeSessionParams();
+ final int expectedKeepalive = isAutomaticNattKeepaliveTimerEnabled
+ ? AUTOMATIC_KEEPALIVE_DELAY_SECONDS
+ : ikeSessionParams.getNattKeepAliveDelaySeconds();
+ final int expectedIpVersion = isAutomaticIpVersionSelectionEnabled
+ ? ESP_IP_VERSION_AUTO
+ : ikeSessionParams.getIpVersion();
+ final int expectedEncapType = isAutomaticIpVersionSelectionEnabled
+ ? ESP_ENCAP_TYPE_AUTO
+ : ikeSessionParams.getEncapType();
+ doTestMigrateIkeSession(ikeProfile.toVpnProfile(), expectedKeepalive,
+ expectedIpVersion, expectedEncapType, nc);
+ }
+
+ @Test
+ public void doTestMigrateIkeSession_Vcn() throws Exception {
+ final int expectedKeepalive = 2097; // Any unlikely number will do
+ final NetworkCapabilities vcnNc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setTransportInfo(new VcnTransportInfo(TEST_SUB_ID, expectedKeepalive))
+ .build();
+ final Ikev2VpnProfile ikev2VpnProfile = makeIkeV2VpnProfile(
+ true /* isAutomaticIpVersionSelectionEnabled */,
+ true /* isAutomaticNattKeepaliveTimerEnabled */,
+ 234 /* keepaliveInProfile */, // Should be ignored, any value will do
+ ESP_IP_VERSION_IPV4, // Should be ignored
+ ESP_ENCAP_TYPE_UDP // Should be ignored
+ );
+ doTestMigrateIkeSession(
+ ikev2VpnProfile.toVpnProfile(),
+ expectedKeepalive,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ vcnNc);
+ }
+
+ private void doTestMigrateIkeSession(
+ @NonNull final VpnProfile profile,
+ final int expectedKeepalive,
+ final int expectedIpVersion,
+ final int expectedEncapType,
+ @NonNull final NetworkCapabilities caps) throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(profile,
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ caps /* underlying network capabilities */,
+ false /* mtuSupportsIpv6 */,
+ expectedKeepalive < DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC);
+ // Simulate a new network coming up
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+ verify(mIkeSessionWrapper, never()).setNetwork(any(), anyInt(), anyInt(), anyInt());
+
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2, caps);
+ // Verify MOBIKE is triggered
+ verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS)).setNetwork(TEST_NETWORK_2,
+ expectedIpVersion, expectedEncapType, expectedKeepalive);
+
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ }
+
+ @Test
+ public void testLinkPropertiesUpdateTriggerReevaluation() throws Exception {
+ final boolean hasV6 = true;
+
+ mockCarrierConfig(TEST_SUB_ID, TelephonyManager.SIM_STATE_LOADED, TEST_KEEPALIVE_TIMER,
+ PREFERRED_IKE_PROTOCOL_IPV6_ESP);
+ final IkeSessionParams params = getTestIkeSessionParams(hasV6,
+ new IkeFqdnIdentification(TEST_IDENTITY), TEST_KEEPALIVE_TIMER);
+ final IkeTunnelConnectionParams tunnelParams =
+ new IkeTunnelConnectionParams(params, CHILD_PARAMS);
+ final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams)
+ .setBypassable(true)
+ .setAutomaticNattKeepaliveTimerEnabled(false)
+ .setAutomaticIpVersionSelectionEnabled(true)
+ .build();
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ new NetworkCapabilities.Builder().build() /* underlying network caps */,
+ hasV6 /* mtuSupportsIpv6 */,
+ false /* areLongLivedTcpConnectionsExpensive */);
+ reset(mExecutor);
+
+ // Simulate a new network coming up
+ final LinkProperties lp = new LinkProperties();
+ lp.addLinkAddress(new LinkAddress("192.0.2.2/32"));
+
+ // Have the executor use the real delay to make sure schedule() was called only
+ // once for all calls. Also, arrange for execute() not to call schedule() to avoid
+ // messing with the checks for schedule().
+ mExecutor.delayMs = TestExecutor.REAL_DELAY;
+ mExecutor.executeDirect = true;
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(
+ TEST_NETWORK_2, new NetworkCapabilities.Builder().build());
+ vpnSnapShot.nwCb.onLinkPropertiesChanged(TEST_NETWORK_2, new LinkProperties(lp));
+ verify(mExecutor).schedule(any(Runnable.class), longThat(it -> it > 0), any());
+ reset(mExecutor);
+
+ final InOrder order = inOrder(mIkeSessionWrapper);
+
+ // Verify the network is started
+ order.verify(mIkeSessionWrapper, timeout(TIMEOUT_CROSSTHREAD_MS)).setNetwork(TEST_NETWORK_2,
+ ESP_IP_VERSION_AUTO, ESP_ENCAP_TYPE_AUTO, TEST_KEEPALIVE_TIMER);
+
+ // Send the same properties, check that no migration is scheduled
+ vpnSnapShot.nwCb.onLinkPropertiesChanged(TEST_NETWORK_2, new LinkProperties(lp));
+ verify(mExecutor, never()).schedule(any(Runnable.class), anyLong(), any());
+
+ // Add v6 address, verify MOBIKE is triggered
+ lp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ vpnSnapShot.nwCb.onLinkPropertiesChanged(TEST_NETWORK_2, new LinkProperties(lp));
+ order.verify(mIkeSessionWrapper, timeout(TIMEOUT_CROSSTHREAD_MS)).setNetwork(TEST_NETWORK_2,
+ ESP_IP_VERSION_AUTO, ESP_ENCAP_TYPE_AUTO, TEST_KEEPALIVE_TIMER);
+
+ // Add another v4 address, verify MOBIKE is triggered
+ final LinkProperties stacked = new LinkProperties();
+ stacked.setInterfaceName("v4-" + lp.getInterfaceName());
+ stacked.addLinkAddress(new LinkAddress("192.168.0.1/32"));
+ lp.addStackedLink(stacked);
+ vpnSnapShot.nwCb.onLinkPropertiesChanged(TEST_NETWORK_2, new LinkProperties(lp));
+ order.verify(mIkeSessionWrapper, timeout(TIMEOUT_CROSSTHREAD_MS)).setNetwork(TEST_NETWORK_2,
+ ESP_IP_VERSION_AUTO, ESP_ENCAP_TYPE_AUTO, TEST_KEEPALIVE_TIMER);
+
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ }
+
+ private void mockCarrierConfig(int subId, int simStatus, int keepaliveTimer, int ikeProtocol) {
+ final SubscriptionInfo subscriptionInfo = mock(SubscriptionInfo.class);
+ doReturn(subId).when(subscriptionInfo).getSubscriptionId();
+ doReturn(List.of(subscriptionInfo)).when(mSubscriptionManager)
+ .getActiveSubscriptionInfoList();
+
+ doReturn(simStatus).when(mTmPerSub).getSimApplicationState();
+ doReturn(TEST_MCCMNC).when(mTmPerSub).getSimOperator(subId);
+
+ final PersistableBundle persistableBundle = new PersistableBundle();
+ persistableBundle.putInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT, keepaliveTimer);
+ persistableBundle.putInt(KEY_PREFERRED_IKE_PROTOCOL_INT, ikeProtocol);
+ // For CarrierConfigManager.isConfigForIdentifiedCarrier check
+ persistableBundle.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+ doReturn(persistableBundle).when(mConfigManager).getConfigForSubId(subId);
+ }
+
+ private CarrierConfigManager.CarrierConfigChangeListener getCarrierConfigListener() {
+ final ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+ verify(mConfigManager).registerCarrierConfigChangeListener(any(), listenerCaptor.capture());
+
+ return listenerCaptor.getValue();
+ }
+
+ @Test
+ public void testNattKeepaliveTimerFromCarrierConfig_noSubId() throws Exception {
+ doTestReadCarrierConfig(new NetworkCapabilities(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ false /* expectedReadFromCarrierConfig*/,
+ true /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ @Test
+ public void testNattKeepaliveTimerFromCarrierConfig_simAbsent() throws Exception {
+ doTestReadCarrierConfig(new NetworkCapabilities.Builder().build(),
+ TelephonyManager.SIM_STATE_ABSENT,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ false /* expectedReadFromCarrierConfig*/,
+ true /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ @Test
+ public void testNattKeepaliveTimerFromCarrierConfig() throws Exception {
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_AUTO,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ true /* expectedReadFromCarrierConfig*/,
+ false /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ @Test
+ public void testNattKeepaliveTimerFromCarrierConfig_NotCell() throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new WifiInfo.Builder().build())
+ .build();
+ doTestReadCarrierConfig(nc,
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ false /* expectedReadFromCarrierConfig*/,
+ true /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ @Test
+ public void testPreferredIpProtocolFromCarrierConfig_v4UDP() throws Exception {
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_IPV4 /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_UDP /* expectedEncapType */,
+ true /* expectedReadFromCarrierConfig*/,
+ false /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ @Test
+ public void testPreferredIpProtocolFromCarrierConfig_v6ESP() throws Exception {
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV6_ESP,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_IPV6 /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_NONE /* expectedEncapType */,
+ true /* expectedReadFromCarrierConfig*/,
+ false /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ @Test
+ public void testPreferredIpProtocolFromCarrierConfig_v6UDP() throws Exception {
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV6_UDP,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_IPV6 /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_UDP /* expectedEncapType */,
+ true /* expectedReadFromCarrierConfig*/,
+ false /* areLongLivedTcpConnectionsExpensive */);
+ }
+
+ private NetworkCapabilities createTestCellNc() {
+ return new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(TEST_SUB_ID)
+ .build())
+ .build();
+ }
+
+ private void doTestReadCarrierConfig(NetworkCapabilities nc, int simState, int preferredIpProto,
+ int expectedKeepaliveTimer, int expectedIpVersion, int expectedEncapType,
+ boolean expectedReadFromCarrierConfig,
+ boolean areLongLivedTcpConnectionsExpensive)
+ throws Exception {
+ final Ikev2VpnProfile ikeProfile =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY)
+ .setAuthPsk(TEST_VPN_PSK)
+ .setBypassable(true /* isBypassable */)
+ .setAutomaticNattKeepaliveTimerEnabled(true)
+ .setAutomaticIpVersionSelectionEnabled(true)
+ .build();
+
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ new NetworkCapabilities.Builder().build() /* underlying network caps */,
+ false /* mtuSupportsIpv6 */,
+ true /* areLongLivedTcpConnectionsExpensive */);
+
+ final CarrierConfigManager.CarrierConfigChangeListener listener =
+ getCarrierConfigListener();
+
+ // Simulate a new network coming up
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+ // Migration will not be started until receiving network capabilities change.
+ verify(mIkeSessionWrapper, never()).setNetwork(any(), anyInt(), anyInt(), anyInt());
+
+ reset(mIkeSessionWrapper);
+ mockCarrierConfig(TEST_SUB_ID, simState, TEST_KEEPALIVE_TIMER, preferredIpProto);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2, nc);
+ verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS)).setNetwork(TEST_NETWORK_2,
+ expectedIpVersion, expectedEncapType, expectedKeepaliveTimer);
+ if (expectedReadFromCarrierConfig) {
+ final ArgumentCaptor<NetworkCapabilities> ncCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
+ verify(mMockNetworkAgent, timeout(TEST_TIMEOUT_MS))
+ .doSendNetworkCapabilities(ncCaptor.capture());
+
+ final VpnTransportInfo info =
+ (VpnTransportInfo) ncCaptor.getValue().getTransportInfo();
+ assertEquals(areLongLivedTcpConnectionsExpensive,
+ info.areLongLivedTcpConnectionsExpensive());
+ } else {
+ verify(mMockNetworkAgent, never()).doSendNetworkCapabilities(any());
+ }
+
+ reset(mExecutor);
+ reset(mIkeSessionWrapper);
+ reset(mMockNetworkAgent);
+
+ // Trigger carrier config change
+ listener.onCarrierConfigChanged(1 /* logicalSlotIndex */, TEST_SUB_ID,
+ -1 /* carrierId */, -1 /* specificCarrierId */);
+ verify(mIkeSessionWrapper).setNetwork(TEST_NETWORK_2,
+ expectedIpVersion, expectedEncapType, expectedKeepaliveTimer);
+ // Expect no NetworkCapabilities change.
+ // Call to doSendNetworkCapabilities() will not be triggered.
+ verify(mMockNetworkAgent, never()).doSendNetworkCapabilities(any());
+ }
+
+ @Test
+ public void testStartPlatformVpn_mtuDoesNotSupportIpv6() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ false /* mtuSupportsIpv6 */);
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ }
+
+ @Test
+ public void testStartPlatformVpn_underlyingNetworkNotChange() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+ // Trigger update on the same network should not cause underlying network change in NC of
+ // the VPN network
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK,
+ new NetworkCapabilities.Builder()
+ .setSubscriptionIds(Set.of(TEST_SUB_ID))
+ .build());
+ // Verify setNetwork() called but no underlying network update
+ verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS)).setNetwork(eq(TEST_NETWORK),
+ eq(ESP_IP_VERSION_AUTO) /* ipVersion */,
+ eq(ESP_ENCAP_TYPE_AUTO) /* encapType */,
+ eq(DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT) /* keepaliveDelay */);
+ verify(mMockNetworkAgent, never())
+ .doSetUnderlyingNetworks(any());
+
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2,
+ new NetworkCapabilities.Builder().build());
+
+ // A new network should trigger both setNetwork() and a underlying network update.
+ verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS)).setNetwork(eq(TEST_NETWORK_2),
+ eq(ESP_IP_VERSION_AUTO) /* ipVersion */,
+ eq(ESP_ENCAP_TYPE_AUTO) /* encapType */,
+ eq(DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT) /* keepaliveDelay */);
+ verify(mMockNetworkAgent).doSetUnderlyingNetworks(
+ Collections.singletonList(TEST_NETWORK_2));
+
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ }
+
+ @Test
+ public void testStartPlatformVpnMobility_mobikeEnabled() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+ // Set new MTU on a different network
+ final int newMtu = IPV6_MIN_MTU + 1;
+ doReturn(newMtu).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(), anyBoolean());
+
+ // Mock network loss and verify a cleanup task is scheduled
+ vpnSnapShot.nwCb.onLost(TEST_NETWORK);
+ verify(mExecutor, atLeastOnce()).schedule(any(Runnable.class), anyLong(), any());
+
+ // Mock new network comes up and the cleanup task is cancelled
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+ verify(mIkeSessionWrapper, never()).setNetwork(any(), anyInt(), anyInt(), anyInt());
+
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2,
+ new NetworkCapabilities.Builder().build());
+ // Verify MOBIKE is triggered
+ verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS)).setNetwork(eq(TEST_NETWORK_2),
+ eq(ESP_IP_VERSION_AUTO) /* ipVersion */,
+ eq(ESP_ENCAP_TYPE_AUTO) /* encapType */,
+ eq(DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT) /* keepaliveDelay */);
+ // Verify mNetworkCapabilities is updated
+ assertEquals(
+ Collections.singletonList(TEST_NETWORK_2),
+ vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+ verify(mMockNetworkAgent)
+ .doSetUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
+
+ // Mock the MOBIKE procedure
+ vpnSnapShot.ikeCb.onIkeSessionConnectionInfoChanged(createIkeConnectInfo_2());
+ vpnSnapShot.childCb.onIpSecTransformsMigrated(
+ createIpSecTransform(), createIpSecTransform());
+
+ verify(mIpSecService).setNetworkForTunnelInterface(
+ eq(TEST_TUNNEL_RESOURCE_ID), eq(TEST_NETWORK_2), anyString());
+
+ // Expect 2 times: one for initial setup and one for MOBIKE
+ verifyApplyTunnelModeTransforms(2);
+
+ // Verify mNetworkAgent is updated
+ verify(mMockNetworkAgent).doSendLinkProperties(argThat(lp -> lp.getMtu() == newMtu));
+ verify(mMockNetworkAgent, never()).unregister();
+ // No further doSetUnderlyingNetworks interaction. The interaction count should stay one.
+ verify(mMockNetworkAgent, times(1)).doSetUnderlyingNetworks(any());
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ }
+
+ @Test
+ public void testStartPlatformVpnMobility_mobikeEnabledMtuDoesNotSupportIpv6() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+ // Set MTU below 1280
+ final int newMtu = IPV6_MIN_MTU - 1;
+ doReturn(newMtu).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(), anyBoolean());
+
+ // Mock new network available & MOBIKE procedures
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2,
+ new NetworkCapabilities.Builder().build());
+ // Verify mNetworkCapabilities is updated
+ verify(mMockNetworkAgent, timeout(TEST_TIMEOUT_MS))
+ .doSetUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
+ assertEquals(
+ Collections.singletonList(TEST_NETWORK_2),
+ vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+
+ vpnSnapShot.ikeCb.onIkeSessionConnectionInfoChanged(createIkeConnectInfo_2());
+ vpnSnapShot.childCb.onIpSecTransformsMigrated(
+ createIpSecTransform(), createIpSecTransform());
+
+ // Verify removal of IPv6 addresses and routes triggers a network agent restart
+ final ArgumentCaptor<LinkProperties> lpCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mTestDeps, times(2))
+ .newNetworkAgent(any(), any(), anyString(), any(), lpCaptor.capture(), any(), any(),
+ any(), any());
+ verify(mMockNetworkAgent).unregister();
+ // mMockNetworkAgent is an old NetworkAgent, so it won't update LinkProperties after
+ // unregistering.
+ verify(mMockNetworkAgent, never()).doSendLinkProperties(any());
+
+ final LinkProperties lp = lpCaptor.getValue();
+
+ for (LinkAddress addr : lp.getLinkAddresses()) {
+ if (addr.isIpv6()) {
+ fail("IPv6 address found on VPN with MTU < IPv6 minimum MTU");
+ }
+ }
+
+ for (InetAddress dnsAddr : lp.getDnsServers()) {
+ if (dnsAddr instanceof Inet6Address) {
+ fail("IPv6 DNS server found on VPN with MTU < IPv6 minimum MTU");
+ }
+ }
+
+ for (RouteInfo routeInfo : lp.getRoutes()) {
+ if (routeInfo.getDestinationLinkAddress().isIpv6()
+ && !routeInfo.isIPv6UnreachableDefault()) {
+ fail("IPv6 route found on VPN with MTU < IPv6 minimum MTU");
+ }
+ }
+
+ assertEquals(newMtu, lp.getMtu());
+
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ }
+
+ @Test
+ public void testStartPlatformVpnReestablishes_mobikeDisabled() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
+
+ // Forget the first IKE creation to be prepared to capture callbacks of the second
+ // IKE session
+ resetIkev2SessionCreator(mock(Vpn.IkeSessionWrapper.class));
+
+ // Mock network switch
+ vpnSnapShot.nwCb.onLost(TEST_NETWORK);
+ vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+ // The old IKE Session will not be killed until receiving network capabilities change.
+ verify(mIkeSessionWrapper, never()).kill();
+
+ vpnSnapShot.nwCb.onCapabilitiesChanged(
+ TEST_NETWORK_2, new NetworkCapabilities.Builder().build());
+ // Verify the old IKE Session is killed
+ verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS)).kill();
+
+ // Capture callbacks of the new IKE Session
+ final Pair<IkeSessionCallback, ChildSessionCallback> cbPair =
+ verifyCreateIkeAndCaptureCbs();
+ final IkeSessionCallback ikeCb = cbPair.first;
+ final ChildSessionCallback childCb = cbPair.second;
+
+ // Mock the IKE Session setup
+ ikeCb.onOpened(createIkeConfig(createIkeConnectInfo_2(), false /* isMobikeEnabled */));
+
+ childCb.onIpSecTransformCreated(createIpSecTransform(), IpSecManager.DIRECTION_IN);
+ childCb.onIpSecTransformCreated(createIpSecTransform(), IpSecManager.DIRECTION_OUT);
+ childCb.onOpened(createChildConfig());
+
+ // Expect 2 times since there have been two Session setups
+ verifyApplyTunnelModeTransforms(2);
+
+ // Verify mNetworkCapabilities and mNetworkAgent are updated
+ assertEquals(
+ Collections.singletonList(TEST_NETWORK_2),
+ vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+ verify(mMockNetworkAgent)
+ .doSetUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
+
+ vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+ }
+
+ private String getDump(@NonNull final Vpn vpn) {
+ final StringWriter sw = new StringWriter();
+ final IndentingPrintWriter writer = new IndentingPrintWriter(sw, "");
+ vpn.dump(writer);
+ writer.flush();
+ return sw.toString();
+ }
+
+ private int countMatches(@NonNull final Pattern regexp, @NonNull final String string) {
+ final Matcher m = regexp.matcher(string);
+ int i = 0;
+ while (m.find()) ++i;
+ return i;
+ }
+
+ @Test
+ public void testNCEventChanges() throws Exception {
+ final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .setLinkDownstreamBandwidthKbps(1000)
+ .setLinkUpstreamBandwidthKbps(500);
+
+ final Ikev2VpnProfile ikeProfile =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY)
+ .setAuthPsk(TEST_VPN_PSK)
+ .setBypassable(true /* isBypassable */)
+ .setAutomaticNattKeepaliveTimerEnabled(true)
+ .setAutomaticIpVersionSelectionEnabled(true)
+ .build();
+
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ ncBuilder.build(), false /* mtuSupportsIpv6 */,
+ true /* areLongLivedTcpConnectionsExpensive */);
+
+ // Calls to onCapabilitiesChanged will be thrown to the executor for execution ; by
+ // default this will incur a 10ms delay before it's executed, messing with the timing
+ // of the log and having the checks for counts in equals() below flake.
+ mExecutor.executeDirect = true;
+
+ // First nc changed triggered by verifySetupPlatformVpn
+ final Pattern pattern = Pattern.compile("Cap changed from", Pattern.MULTILINE);
+ final String stage1 = getDump(vpnSnapShot.vpn);
+ assertEquals(1, countMatches(pattern, stage1));
+
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage2 = getDump(vpnSnapShot.vpn);
+ // Was the same caps, there should still be only 1 match
+ assertEquals(1, countMatches(pattern, stage2));
+
+ ncBuilder.setLinkDownstreamBandwidthKbps(1200)
+ .setLinkUpstreamBandwidthKbps(300);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage3 = getDump(vpnSnapShot.vpn);
+ // Was not an important change, should not be logged, still only 1 match
+ assertEquals(1, countMatches(pattern, stage3));
+
+ ncBuilder.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage4 = getDump(vpnSnapShot.vpn);
+ // Change to caps is important, should cause a new match
+ assertEquals(2, countMatches(pattern, stage4));
+
+ ncBuilder.removeCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ ncBuilder.setLinkDownstreamBandwidthKbps(600);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage5 = getDump(vpnSnapShot.vpn);
+ // Change to caps is important, should cause a new match even with the unimportant change
+ assertEquals(3, countMatches(pattern, stage5));
+ }
+ // TODO : beef up event logs tests
+
+ private void verifyHandlingNetworkLoss(PlatformVpnSnapshot vpnSnapShot) throws Exception {
+ // Forget the #sendLinkProperties during first setup.
+ reset(mMockNetworkAgent);
+
+ // Mock network loss
+ vpnSnapShot.nwCb.onLost(TEST_NETWORK);
+
+ // Mock the grace period expires
+ verify(mExecutor, atLeastOnce()).schedule(any(Runnable.class), anyLong(), any());
+
+ final ArgumentCaptor<LinkProperties> lpCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mMockNetworkAgent, timeout(TEST_TIMEOUT_MS))
+ .doSendLinkProperties(lpCaptor.capture());
+ final LinkProperties lp = lpCaptor.getValue();
+
+ assertNull(lp.getInterfaceName());
+ final List<RouteInfo> expectedRoutes = Arrays.asList(
+ new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /* gateway */,
+ null /* iface */, RTN_UNREACHABLE),
+ new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /* gateway */,
+ null /* iface */, RTN_UNREACHABLE));
+ assertEquals(expectedRoutes, lp.getRoutes());
+
+ verify(mMockNetworkAgent, timeout(TEST_TIMEOUT_MS)).unregister();
+ }
+
+ @Test
+ public void testStartPlatformVpnHandlesNetworkLoss_mobikeEnabled() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+ verifyHandlingNetworkLoss(vpnSnapShot);
+ }
+
+ @Test
+ public void testStartPlatformVpnHandlesNetworkLoss_mobikeDisabled() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
+ verifyHandlingNetworkLoss(vpnSnapShot);
+ }
+
+ private ConnectivityDiagnosticsCallback getConnectivityDiagCallback() {
+ final ArgumentCaptor<ConnectivityDiagnosticsCallback> cdcCaptor =
+ ArgumentCaptor.forClass(ConnectivityDiagnosticsCallback.class);
+ verify(mCdm).registerConnectivityDiagnosticsCallback(
+ any(), any(), cdcCaptor.capture());
+ return cdcCaptor.getValue();
+ }
+
+ private DataStallReport createDataStallReport() {
+ return new DataStallReport(TEST_NETWORK, 1234 /* reportTimestamp */,
+ 1 /* detectionMethod */, new LinkProperties(), new NetworkCapabilities(),
+ new PersistableBundle());
+ }
+
+ private void verifyMobikeTriggered(List<Network> expected, int retryIndex) {
+ // Verify retry is scheduled
+ final long expectedDelayMs = mTestDeps.getValidationFailRecoveryMs(retryIndex);
+ final ArgumentCaptor<Long> delayCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mExecutor, times(retryIndex + 1)).schedule(
+ any(Runnable.class), delayCaptor.capture(), eq(TimeUnit.MILLISECONDS));
+ final List<Long> delays = delayCaptor.getAllValues();
+ assertEquals(expectedDelayMs, (long) delays.get(delays.size() - 1));
+
+ final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+ verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS + expectedDelayMs))
+ .setNetwork(networkCaptor.capture(), anyInt() /* ipVersion */,
+ anyInt() /* encapType */, anyInt() /* keepaliveDelay */);
+ assertEquals(expected, Collections.singletonList(networkCaptor.getValue()));
+ }
+
+ @Test
+ public void testDataStallInIkev2VpnMobikeDisabled() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
+
+ doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+
+ // Should not trigger MOBIKE if MOBIKE is not enabled
+ verify(mIkeSessionWrapper, never()).setNetwork(any() /* network */,
+ anyInt() /* ipVersion */, anyInt() /* encapType */, anyInt() /* keepaliveDelay */);
+ }
+
+ @Test
+ public void testDataStallInIkev2VpnRecoveredByMobike() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+ doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ // Verify MOBIKE is triggered
+ verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
+ 0 /* retryIndex */);
+ // Validation failure on VPN network should trigger a re-evaluation request for the
+ // underlying network.
+ verify(mConnectivityManager).reportNetworkConnectivity(TEST_NETWORK, false);
+
+ reset(mIkev2SessionCreator);
+ reset(mExecutor);
+
+ // Send validation status update.
+ // Recovered and get network validated. It should not trigger the ike session reset.
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_VALID);
+ // Verify that the retry count is reset. The mValidationFailRetryCount will not be reset
+ // until the executor finishes the execute() call, so wait until the all tasks are executed.
+ waitForIdleSerialExecutor(mExecutor, TEST_TIMEOUT_MS);
+ assertEquals(0,
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).mValidationFailRetryCount);
+ verify(mIkev2SessionCreator, never()).createIkeSession(
+ any(), any(), any(), any(), any(), any());
+
+ reset(mIkeSessionWrapper);
+ reset(mExecutor);
+
+ // Another validation fail should trigger another reportNetworkConnectivity
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
+ 0 /* retryIndex */);
+ verify(mConnectivityManager, times(2)).reportNetworkConnectivity(TEST_NETWORK, false);
+ }
+
+ @Test
+ public void testDataStallInIkev2VpnNotRecoveredByMobike() throws Exception {
+ final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+ int retry = 0;
+ doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
+ retry++);
+ // Validation failure on VPN network should trigger a re-evaluation request for the
+ // underlying network.
+ verify(mConnectivityManager).reportNetworkConnectivity(TEST_NETWORK, false);
+ reset(mIkev2SessionCreator);
+
+ // Second validation status update.
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks(),
+ retry++);
+ // Call to reportNetworkConnectivity should only happen once. No further interaction.
+ verify(mConnectivityManager, times(1)).reportNetworkConnectivity(TEST_NETWORK, false);
+
+ // Use real delay to verify reset session will not be performed if there is an existing
+ // recovery for resetting the session.
+ mExecutor.delayMs = TestExecutor.REAL_DELAY;
+ mExecutor.executeDirect = true;
+ // Send validation status update should result in ike session reset.
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+
+ // Verify session reset is scheduled
+ long expectedDelay = mTestDeps.getValidationFailRecoveryMs(retry++);
+ final ArgumentCaptor<Long> delayCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mExecutor, times(retry)).schedule(any(Runnable.class), delayCaptor.capture(),
+ eq(TimeUnit.MILLISECONDS));
+ final List<Long> delays = delayCaptor.getAllValues();
+ assertEquals(expectedDelay, (long) delays.get(delays.size() - 1));
+ // Call to reportNetworkConnectivity should only happen once. No further interaction.
+ verify(mConnectivityManager, times(1)).reportNetworkConnectivity(TEST_NETWORK, false);
+
+ // Another invalid status reported should not trigger other scheduled recovery.
+ expectedDelay = mTestDeps.getValidationFailRecoveryMs(retry++);
+ ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ verify(mExecutor, never()).schedule(
+ any(Runnable.class), eq(expectedDelay), eq(TimeUnit.MILLISECONDS));
+
+ // Verify that session being reset
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS + expectedDelay))
+ .createIkeSession(any(), any(), any(), any(), any(), any());
+ // Call to reportNetworkConnectivity should only happen once. No further interaction.
+ verify(mConnectivityManager, times(1)).reportNetworkConnectivity(TEST_NETWORK, false);
+ }
+
+ @Test
+ public void testStartLegacyVpnType() throws Exception {
+ setMockedUsers(PRIMARY_USER);
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final VpnProfile profile = new VpnProfile("testProfile" /* key */);
+
+ profile.type = VpnProfile.TYPE_PPTP;
+ assertThrows(UnsupportedOperationException.class, () -> startLegacyVpn(vpn, profile));
+ profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK;
+ assertThrows(UnsupportedOperationException.class, () -> startLegacyVpn(vpn, profile));
+ }
+
+ @Test
+ public void testStartLegacyVpnModifyProfile_TypePSK() throws Exception {
+ setMockedUsers(PRIMARY_USER);
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final Ikev2VpnProfile ikev2VpnProfile =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY)
+ .setAuthPsk(TEST_VPN_PSK)
+ .build();
+ final VpnProfile profile = ikev2VpnProfile.toVpnProfile();
+
+ startLegacyVpn(vpn, profile);
+ assertEquals(profile, ikev2VpnProfile.toVpnProfile());
+ }
+
+ private void assertTransportInfoMatches(NetworkCapabilities nc, int type) {
+ assertNotNull(nc);
+ VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo();
+ assertNotNull(ti);
+ assertEquals(type, ti.getType());
+ }
+
+ // Make it public and un-final so as to spy it
+ public class TestDeps extends Vpn.Dependencies {
+ TestDeps() {}
+
+ @Override
+ public boolean isCallerSystem() {
+ return true;
+ }
+
+ @Override
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return null;
+ }
+
+ @Override
+ public ParcelFileDescriptor adoptFd(Vpn vpn, int mtu) {
+ return new ParcelFileDescriptor(new FileDescriptor());
+ }
+
+ @Override
+ public int jniCreate(Vpn vpn, int mtu) {
+ // Pick a random positive number as fd to return.
+ return 345;
+ }
+
+ @Override
+ public String jniGetName(Vpn vpn, int fd) {
+ return TEST_IFACE_NAME;
+ }
+
+ @Override
+ public int jniSetAddresses(Vpn vpn, String interfaze, String addresses) {
+ if (addresses == null) return 0;
+ // Return the number of addresses.
+ return addresses.split(" ").length;
+ }
+
+ @Override
+ public void setBlocking(FileDescriptor fd, boolean blocking) {}
+
+ @Override
+ public DeviceIdleInternal getDeviceIdleInternal() {
+ return mDeviceIdleInternal;
+ }
+
+ @Override
+ public long getValidationFailRecoveryMs(int retryCount) {
+ // Simply return retryCount as the delay seconds for retrying.
+ return retryCount * 100L;
+ }
+
+ @Override
+ public ScheduledThreadPoolExecutor newScheduledThreadPoolExecutor() {
+ return mExecutor;
+ }
+
+ public boolean mIgnoreCallingUidChecks = true;
+ @Override
+ public void verifyCallingUidAndPackage(Context context, String packageName, int userId) {
+ if (!mIgnoreCallingUidChecks) {
+ super.verifyCallingUidAndPackage(context, packageName, userId);
+ }
+ }
+ }
+
+ /**
+ * Mock some methods of vpn object.
+ */
+ private Vpn createVpn(@UserIdInt int userId) {
+ final Context asUserContext = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+ doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
+ when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
+ .thenReturn(asUserContext);
+ final TestLooper testLooper = new TestLooper();
+ final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, mTestDeps, mNetService,
+ mNetd, userId, mVpnProfileStore, mSystemServices, mIkev2SessionCreator);
+ verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat(
+ provider -> provider.getName().contains("VpnNetworkProvider")
+ ));
+ return vpn;
+ }
+
+ /**
+ * Populate {@link #mUserManager} with a list of fake users.
+ */
+ private void setMockedUsers(UserInfo... users) {
+ final Map<Integer, UserInfo> userMap = new ArrayMap<>();
+ for (UserInfo user : users) {
+ userMap.put(user.id, user);
+ }
+
+ /**
+ * @see UserManagerService#getUsers(boolean)
+ */
+ doAnswer(invocation -> {
+ final ArrayList<UserInfo> result = new ArrayList<>(users.length);
+ for (UserInfo ui : users) {
+ if (ui.isEnabled() && !ui.partial) {
+ result.add(ui);
+ }
+ }
+ return result;
+ }).when(mUserManager).getAliveUsers();
+
+ doAnswer(invocation -> {
+ final int id = (int) invocation.getArguments()[0];
+ return userMap.get(id);
+ }).when(mUserManager).getUserInfo(anyInt());
+ }
+
+ /**
+ * Populate {@link #mPackageManager} with a fake packageName-to-UID mapping.
+ */
+ private void setMockedPackages(final Map<String, Integer> packages) {
+ try {
+ doAnswer(invocation -> {
+ final String appName = (String) invocation.getArguments()[0];
+ final int userId = (int) invocation.getArguments()[1];
+ Integer appId = packages.get(appName);
+ if (appId == null) throw new PackageManager.NameNotFoundException(appName);
+ return UserHandle.getUid(userId, appId);
+ }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt());
+ } catch (Exception e) {
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java b/services/tests/VpnTests/java/com/android/server/net/LockdownVpnTrackerTest.java
index 0e881efd4cdf..0e881efd4cdf 100644
--- a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
+++ b/services/tests/VpnTests/java/com/android/server/net/LockdownVpnTrackerTest.java
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
index fbb14c3db9f9..440905137cf9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManager;
import org.junit.Before;
import org.junit.Test;
@@ -66,8 +65,6 @@ public class DisplayOffloadSessionImplTest {
mSession.stopOffload();
assertFalse(mSession.isActive());
- verify(mDisplayPowerController).setBrightnessFromOffload(
- PowerManager.BRIGHTNESS_INVALID_FLOAT);
// An inactive session shouldn't be stopped again
mSession.stopOffload();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index fcee70fd0702..14d8a9c5f0f2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -22,6 +22,7 @@ import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIG
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -77,6 +78,7 @@ import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.color.ColorDisplayService;
@@ -1559,6 +1561,43 @@ public final class DisplayPowerControllerTest {
verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ assertEquals(BrightnessReason.REASON_OFFLOAD, mHolder.dpc.mBrightnessReason.getReason());
+ }
+
+ @Test
+ public void testBrightness_AutomaticHigherPriorityThanOffload() {
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ float brightness = 0.34f;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ mHolder.dpc.setBrightnessFromOffload(brightness);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ assertEquals(BrightnessReason.REASON_OFFLOAD, mHolder.dpc.mBrightnessReason.getReason());
+
+ // Now automatic brightness becomes available
+ brightness = 0.22f;
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(brightness);
+
+ mHolder.dpc.updateBrightness();
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ assertEquals(BrightnessReason.REASON_AUTOMATIC, mHolder.dpc.mBrightnessReason.getReason());
}
@Test
@@ -1727,6 +1766,27 @@ public final class DisplayPowerControllerTest {
/* ignoreAnimationLimits= */ anyBoolean());
}
+ @Test
+ public void testDefaultDozeBrightness() {
+ float brightness = 0.121f;
+ when(mPowerManagerMock.getBrightnessConstraint(
+ PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)).thenReturn(brightness);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness), /* linearSecondTarget= */ anyFloat(),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index b99ecf3978ae..14de527aa1f7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -46,7 +46,6 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.os.PowerManager;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.SurfaceControl;
@@ -1229,8 +1228,6 @@ public class LocalDisplayAdapterTest {
verify(mDisplayOffloader).stopOffload();
assertFalse(mDisplayOffloadSession.isActive());
- verify(mMockedDisplayPowerController).setBrightnessFromOffload(
- PowerManager.BRIGHTNESS_INVALID_FLOAT);
}
private void initDisplayOffloadSession() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index 060f99b4317a..397d77c52f68 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -16,6 +16,8 @@
package com.android.server.display.brightness;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import static org.junit.Assert.assertEquals;
@@ -43,6 +45,7 @@ public final class BrightnessEventTest {
getReason(BrightnessReason.REASON_DOZE, BrightnessReason.MODIFIER_LOW_POWER));
mBrightnessEvent.setPhysicalDisplayId("test");
mBrightnessEvent.setDisplayState(Display.STATE_ON);
+ mBrightnessEvent.setDisplayPolicy(POLICY_BRIGHT);
mBrightnessEvent.setLux(100.0f);
mBrightnessEvent.setPreThresholdLux(150.0f);
mBrightnessEvent.setTime(System.currentTimeMillis());
@@ -74,11 +77,12 @@ public final class BrightnessEventTest {
public void testToStringWorksAsExpected() {
String actualString = mBrightnessEvent.toString(false);
String expectedString =
- "BrightnessEvent: disp=1, physDisp=test, displayState=ON, brt=0.6, initBrt=25.0,"
- + " rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off,"
- + " rbcStrength=-1, thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true,"
- + " flags=, reason=doze [ low_pwr ], autoBrightness=true, strategy="
- + DISPLAY_BRIGHTNESS_STRATEGY_NAME + ", autoBrightnessMode=idle";
+ "BrightnessEvent: disp=1, physDisp=test, displayState=ON, displayPolicy=BRIGHT,"
+ + " brt=0.6, initBrt=25.0, rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0,"
+ + " hbmMax=0.62, hbmMode=off, rbcStrength=-1, thrmMax=0.65, powerFactor=0.2,"
+ + " wasShortTermModelActive=true, flags=, reason=doze [ low_pwr ],"
+ + " autoBrightness=true, strategy=" + DISPLAY_BRIGHTNESS_STRATEGY_NAME
+ + ", autoBrightnessMode=idle";
assertEquals(expectedString, actualString);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 289d54b86c7d..9b6cc0a4b377 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -393,7 +393,31 @@ public final class DisplayBrightnessControllerTest {
OffloadBrightnessStrategy offloadBrightnessStrategy = mock(OffloadBrightnessStrategy.class);
when(mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()).thenReturn(
offloadBrightnessStrategy);
- mDisplayBrightnessController.setBrightnessFromOffload(brightness);
+ boolean brightnessUpdated =
+ mDisplayBrightnessController.setBrightnessFromOffload(brightness);
verify(offloadBrightnessStrategy).setOffloadScreenBrightness(brightness);
+ assertTrue(brightnessUpdated);
+ }
+
+ @Test
+ public void setBrightnessFromOffload_OffloadStrategyNull() {
+ float brightness = 0.4f;
+ when(mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()).thenReturn(null);
+ boolean brightnessUpdated =
+ mDisplayBrightnessController.setBrightnessFromOffload(brightness);
+ assertFalse(brightnessUpdated);
+ }
+
+ @Test
+ public void setBrightnessFromOffload_BrightnessUnchanged() {
+ float brightness = 0.4f;
+ OffloadBrightnessStrategy offloadBrightnessStrategy = mock(OffloadBrightnessStrategy.class);
+ when(offloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(brightness);
+ when(mDisplayBrightnessStrategySelector.getOffloadBrightnessStrategy()).thenReturn(
+ offloadBrightnessStrategy);
+ boolean brightnessUpdated =
+ mDisplayBrightnessController.setBrightnessFromOffload(brightness);
+ verify(offloadBrightnessStrategy, never()).setOffloadScreenBrightness(brightness);
+ assertFalse(brightnessUpdated);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index 37958faed1ca..0e89d8383a8f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
import android.view.Display;
import androidx.test.core.app.ApplicationProvider;
@@ -155,6 +156,7 @@ public final class DisplayBrightnessStrategySelectorTest {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ displayPowerRequest.dozeScreenBrightness = 0.2f;
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
@@ -162,6 +164,18 @@ public final class DisplayBrightnessStrategySelectorTest {
}
@Test
+ public void selectStrategyDoesNotSelectDozeStrategyWhenInvalidBrightness() {
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ displayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
+ DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
+ assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
+ Display.STATE_DOZE), mDozeBrightnessModeStrategy);
+ }
+
+ @Test
public void selectStrategySelectsScreenOffStrategyWhenValid() {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
@@ -233,6 +247,7 @@ public final class DisplayBrightnessStrategySelectorTest {
displayPowerRequest.screenBrightnessOverride = Float.NaN;
when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+ when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(true);
when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
assertEquals(mOffloadBrightnessStrategy, mDisplayBrightnessStrategySelector.selectStrategy(
displayPowerRequest, Display.STATE_ON));
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 886780655de2..a5dc668944df 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -188,34 +188,11 @@ public class AutomaticBrightnessStrategyTest {
}
@Test
- public void testAutoBrightnessState_BrightnessReasonIsOffload() {
- mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
- int targetDisplayState = Display.STATE_ON;
- boolean allowAutoBrightnessWhileDozing = false;
- int brightnessReason = BrightnessReason.REASON_OFFLOAD;
- int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
- float lastUserSetBrightness = 0.2f;
- boolean userSetBrightnessChanged = true;
- mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
- mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
- verify(mAutomaticBrightnessController)
- .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
- mBrightnessConfiguration,
- lastUserSetBrightness,
- userSetBrightnessChanged, 0.5f,
- false, policy, true);
- assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
- assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
- }
-
- @Test
public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() {
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
int targetDisplayState = Display.STATE_DOZE;
boolean allowAutoBrightnessWhileDozing = true;
- int brightnessReason = BrightnessReason.REASON_DOZE;
+ int brightnessReason = BrightnessReason.REASON_UNKNOWN;
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
float lastUserSetBrightness = 0.2f;
boolean userSetBrightnessChanged = true;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 3f6117be6143..e141faf599fa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -2001,6 +2001,59 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
}
@Test
+ public void testReplacePendingToCachedProcess_withDeferrableBroadcast() throws Exception {
+ // Legacy stack doesn't support deferral
+ Assume.assumeTrue(mImpl == Impl.MODERN);
+
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+ final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
+
+ setProcessFreezable(receiverGreenApp, true, false);
+ mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
+ waitForIdle();
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK)
+ .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ final BroadcastOptions opts = BroadcastOptions.makeBasic()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
+
+ final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 10);
+ final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 5);
+ final BroadcastFilter receiverYellow = makeRegisteredReceiver(receiverYellowApp, 0);
+ enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, opts, List.of(
+ receiverGreen, receiverBlue, receiverYellow)));
+
+ // Enqueue the broadcast again to replace the earlier one
+ enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, opts, List.of(
+ receiverGreen, receiverBlue, receiverYellow)));
+
+ waitForIdle();
+ // Green should still be in the cached state and shouldn't receive the broadcast
+ verifyScheduleRegisteredReceiver(never(), receiverGreenApp, timeTick);
+
+ final IApplicationThread blueThread = receiverBlueApp.getThread();
+ final IApplicationThread yellowThread = receiverYellowApp.getThread();
+ final InOrder inOrder = inOrder(blueThread, yellowThread);
+ inOrder.verify(blueThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+ inOrder.verify(yellowThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+
+ setProcessFreezable(receiverGreenApp, false, false);
+ mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
+ waitForIdle();
+
+ // Confirm that green receives the broadcast once it comes out of the cached state
+ verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
+ }
+
+ @Test
public void testIdleAndBarrier() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 6bcd778c234b..c6a6865f1cf1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -60,10 +60,13 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.job.JobInfo;
@@ -71,12 +74,15 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.net.NetworkRequest;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotMapping;
import android.util.ArraySet;
import android.util.EmptyArray;
import android.util.SparseArray;
@@ -104,6 +110,9 @@ import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.Executor;
public class FlexibilityControllerTest {
@@ -113,6 +122,9 @@ public class FlexibilityControllerTest {
private MockitoSession mMockingSession;
private BroadcastReceiver mBroadcastReceiver;
+ private final SparseArray<ArraySet<String>> mCarrierPrivilegedApps = new SparseArray<>();
+ private final SparseArray<TelephonyManager.CarrierPrivilegesCallback>
+ mCarrierPrivilegedCallbacks = new SparseArray<>();
private FlexibilityController mFlexibilityController;
private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private JobStore mJobStore;
@@ -130,6 +142,10 @@ public class FlexibilityControllerTest {
@Mock
private PrefetchController mPrefetchController;
@Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private IPackageManager mIPackageManager;
+ @Mock
private PackageManager mPackageManager;
@Before
@@ -138,6 +154,7 @@ public class FlexibilityControllerTest {
.initMocks(this)
.strictness(Strictness.LENIENT)
.spyStatic(DeviceConfig.class)
+ .mockStatic(AppGlobals.class)
.mockStatic(LocalServices.class)
.startMocking();
// Called in StateController constructor.
@@ -167,17 +184,23 @@ public class FlexibilityControllerTest {
-> mDeviceConfigPropertiesBuilder.build())
.when(() -> DeviceConfig.getProperties(
eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.<String>any()));
+ // Used in FlexibilityController.SpecialAppTracker.
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION))
+ .thenReturn(true);
//used to get jobs by UID
mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir());
doReturn(mJobStore).when(mJobSchedulerService).getJobStore();
// Used in JobStatus.
- doReturn(mock(PackageManagerInternal.class))
- .when(() -> LocalServices.getService(PackageManagerInternal.class));
+ doReturn(mIPackageManager).when(AppGlobals::getPackageManager);
// Freeze the clocks at a moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
+ // Set empty set of privileged apps.
+ setSimSlotMappings(null);
+ setPowerWhitelistExceptIdle();
// Initialize real objects.
doReturn(Long.MAX_VALUE).when(mPrefetchController).getNextEstimatedLaunchTimeLocked(any());
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
@@ -249,9 +272,13 @@ public class FlexibilityControllerTest {
}
private JobStatus createJobStatus(String testTag, JobInfo.Builder job) {
+ return createJobStatus(testTag, job, SOURCE_PACKAGE);
+ }
+
+ private JobStatus createJobStatus(String testTag, JobInfo.Builder job, String sourcePackage) {
JobInfo jobInfo = job.build();
JobStatus js = JobStatus.createFromJobInfo(
- jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, "FCTest", testTag);
+ jobInfo, 1000, sourcePackage, SOURCE_USER_ID, "FCTest", testTag);
js.enqueueTime = FROZEN_TIME;
js.setStandbyBucket(ACTIVE_INDEX);
if (js.hasFlexibilityConstraint()) {
@@ -1084,7 +1111,6 @@ public class FlexibilityControllerTest {
@Test
public void testAllowlistedAppBypass() {
- setPowerWhitelistExceptIdle();
mFlexibilityController.onSystemServicesReady();
JobStatus jsHigh = createJobStatus("testAllowlistedAppBypass",
@@ -1118,6 +1144,148 @@ public class FlexibilityControllerTest {
}
@Test
+ public void testCarrierPrivilegedAppBypass() throws Exception {
+ mFlexibilityController.onSystemServicesReady();
+
+ final String carrier1Pkg1 = "com.test.carrier.1.pkg.1";
+ final String carrier1Pkg2 = "com.test.carrier.1.pkg.2";
+ final String carrier2Pkg = "com.test.carrier.2.pkg";
+ final String nonCarrierPkg = "com.test.normal.pkg";
+
+ setPackageUid(carrier1Pkg1, 1);
+ setPackageUid(carrier1Pkg2, 11);
+ setPackageUid(carrier2Pkg, 2);
+ setPackageUid(nonCarrierPkg, 3);
+
+ // Set the second carrier's privileged list before SIM configuration is sent to test
+ // initialization.
+ setCarrierPrivilegedAppList(2, carrier2Pkg);
+
+ UiccSlotMapping sim1 = mock(UiccSlotMapping.class);
+ UiccSlotMapping sim2 = mock(UiccSlotMapping.class);
+ doReturn(1).when(sim1).getLogicalSlotIndex();
+ doReturn(2).when(sim2).getLogicalSlotIndex();
+ setSimSlotMappings(List.of(sim1, sim2));
+
+ JobStatus jsHighC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier1Pkg1);
+ JobStatus jsDefaultC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier1Pkg1);
+ JobStatus jsLowC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier1Pkg1);
+ JobStatus jsMinC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier1Pkg1);
+ JobStatus jsHighC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier1Pkg2);
+ JobStatus jsDefaultC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier1Pkg2);
+ JobStatus jsLowC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier1Pkg2);
+ JobStatus jsMinC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier1Pkg2);
+ JobStatus jsHighC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier2Pkg);
+ JobStatus jsDefaultC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier2Pkg);
+ JobStatus jsLowC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier2Pkg);
+ JobStatus jsMinC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier2Pkg);
+ JobStatus jsHighNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), nonCarrierPkg);
+ JobStatus jsDefaultNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), nonCarrierPkg);
+ JobStatus jsLowNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), nonCarrierPkg);
+ JobStatus jsMinNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), nonCarrierPkg);
+
+ setCarrierPrivilegedAppList(1);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+
+ // Only mark the first package of carrier 1 as privileged. Only that app's jobs should
+ // be exempted.
+ setCarrierPrivilegedAppList(1, carrier1Pkg1);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+
+ // Add the second package of carrier 1. Both apps' jobs should be exempted.
+ setCarrierPrivilegedAppList(1, carrier1Pkg1, carrier1Pkg2);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+
+ // Remove a SIM slot. The relevant app's should no longer have exempted jobs.
+ setSimSlotMappings(List.of(sim1));
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+ }
+
+ @Test
public void testForegroundAppBypass() {
JobStatus jsHigh = createJobStatus("testAllowlistedAppBypass",
createJob(0).setPriority(JobInfo.PRIORITY_HIGH));
@@ -1753,6 +1921,24 @@ public class FlexibilityControllerTest {
}
}
+ private void setCarrierPrivilegedAppList(int logicalIndex, String... packages) {
+ final ArraySet<String> packageSet = packages == null
+ ? new ArraySet<>() : new ArraySet<>(packages);
+ mCarrierPrivilegedApps.put(logicalIndex, packageSet);
+
+ TelephonyManager.CarrierPrivilegesCallback callback =
+ mCarrierPrivilegedCallbacks.get(logicalIndex);
+ if (callback != null) {
+ callback.onCarrierPrivilegesChanged(packageSet, Collections.emptySet());
+ waitForQuietModuleThread();
+ }
+ }
+
+ private void setPackageUid(final String pkgName, final int uid) throws Exception {
+ doReturn(uid).when(mIPackageManager)
+ .getPackageUid(eq(pkgName), anyLong(), eq(UserHandle.getUserId(uid)));
+ }
+
private void setPowerWhitelistExceptIdle(String... packages) {
doReturn(packages == null ? EmptyArray.STRING : packages)
.when(mDeviceIdleInternal).getFullPowerWhitelistExceptIdle();
@@ -1763,6 +1949,47 @@ public class FlexibilityControllerTest {
}
}
+ private void setSimSlotMappings(@Nullable Collection<UiccSlotMapping> simSlotMapping) {
+ clearInvocations(mTelephonyManager);
+ final Collection<UiccSlotMapping> returnedMapping = simSlotMapping == null
+ ? Collections.emptyList() : simSlotMapping;
+ doReturn(returnedMapping).when(mTelephonyManager).getSimSlotMapping();
+ if (mBroadcastReceiver != null) {
+ final Intent intent = new Intent(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED);
+ mBroadcastReceiver.onReceive(mContext, intent);
+ waitForQuietModuleThread();
+ }
+ if (returnedMapping.size() > 0) {
+ ArgumentCaptor<TelephonyManager.CarrierPrivilegesCallback> callbackCaptor =
+ ArgumentCaptor.forClass(TelephonyManager.CarrierPrivilegesCallback.class);
+ ArgumentCaptor<Integer> logicalIndexCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ final int minExpectedNewRegistrations = Math.max(0,
+ returnedMapping.size() - mCarrierPrivilegedCallbacks.size());
+ verify(mTelephonyManager, atLeast(minExpectedNewRegistrations))
+ .registerCarrierPrivilegesCallback(
+ logicalIndexCaptor.capture(), any(), callbackCaptor.capture());
+
+ final List<Integer> registeredIndices = logicalIndexCaptor.getAllValues();
+ final List<TelephonyManager.CarrierPrivilegesCallback> registeredCallbacks =
+ callbackCaptor.getAllValues();
+ for (int i = 0; i < registeredIndices.size(); ++i) {
+ final int logicalIndex = registeredIndices.get(i);
+ final TelephonyManager.CarrierPrivilegesCallback callback =
+ registeredCallbacks.get(i);
+
+ mCarrierPrivilegedCallbacks.put(logicalIndex, callback);
+
+ // The API contract promises a callback upon registration with the current list.
+ final ArraySet<String> cpApps = mCarrierPrivilegedApps.get(logicalIndex);
+ callback.onCarrierPrivilegesChanged(
+ cpApps == null ? Collections.emptySet() : cpApps,
+ Collections.emptySet());
+ }
+ waitForQuietModuleThread();
+ }
+ }
+
private void setUidBias(int uid, int bias) {
int prevBias = mJobSchedulerService.getUidBias(uid);
doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
diff --git a/packages/SystemUI/compose/features/tests/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
index 69b18c45aed6..e94b8ad0a9ac 100644
--- a/packages/SystemUI/compose/features/tests/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Android Open Source Project
+// Copyright (C) 2024 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.
@@ -13,38 +13,44 @@
// limitations under the License.
package {
- default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+ default_applicable_licenses: ["frameworks_base_license"],
}
-// TODO(b/230606318): Make those host tests instead of device tests.
android_test {
- name: "SystemUIComposeFeaturesTests",
- use_resource_processor: true,
- manifest: "AndroidManifest.xml",
- test_suites: ["device-tests"],
- sdk_version: "current",
- certificate: "platform",
+ name: "RollbackPackageHealthObserverTests",
srcs: [
- "src/**/*.kt",
+ "*.java",
],
static_libs: [
- "SystemUIComposeFeatures",
-
"androidx.test.runner",
- "androidx.test.ext.junit",
+ "mockito-target-extended-minus-junit4",
+ "services.core",
+ "truth",
+ "flag-junit",
+ ],
- "androidx.compose.runtime_runtime",
- "androidx.compose.ui_ui-test-junit4",
- "androidx.compose.ui_ui-test-manifest",
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
],
- kotlincflags: ["-Xjvm-default=all"],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml
new file mode 100644
index 000000000000..c52dbdee4b4b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.rollback">
+
+ <uses-sdk android:targetSdkVersion="35" />
+
+ <application android:testOnly="true"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.rollback"
+ android:label="Frameworks Rollback Package Health Observer test" />
+</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml
new file mode 100644
index 000000000000..635183c553bf
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs Rollback Package Health Observer Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="RollbackPackageHealthObserverTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="RollbackPackageHealthObserverTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server.rollback" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
index e42bdad97730..6ac56bfc254a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"postsubmit": [
{
- "name": "FrameworksMockingServicesTests",
+ "name": "RollbackPackageHealthObserverTests",
"options": [
{
"include-filter": "com.android.server.rollback"
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
new file mode 100644
index 000000000000..7ecc7fd1b94b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wallpaper;
+
+import static android.app.WallpaperManager.LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
+import static android.app.WallpaperManager.PORTRAIT;
+import static android.app.WallpaperManager.SQUARE_LANDSCAPE;
+import static android.app.WallpaperManager.SQUARE_PORTRAIT;
+import static android.app.WallpaperManager.getOrientation;
+import static android.app.WallpaperManager.getRotatedOrientation;
+
+import static com.android.window.flags.Flags.FLAG_MULTI_CROP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.util.SparseArray;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Unit tests for the most important helpers of {@link WallpaperCropper}, in particular
+ * {@link WallpaperCropper#getCrop(Point, Point, SparseArray, boolean)}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(FLAG_MULTI_CROP)
+public class WallpaperCropperTest {
+
+ @Mock
+ private WallpaperDisplayHelper mWallpaperDisplayHelper;
+ private WallpaperCropper mWallpaperCropper;
+
+ private static final Point PORTRAIT_ONE = new Point(500, 800);
+ private static final Point PORTRAIT_TWO = new Point(400, 1000);
+ private static final Point PORTRAIT_THREE = new Point(2000, 800);
+ private static final Point PORTRAIT_FOUR = new Point(1600, 1000);
+
+ private static final Point SQUARE_PORTRAIT_ONE = new Point(1000, 800);
+ private static final Point SQUARE_LANDSCAPE_ONE = new Point(800, 1000);
+
+ /**
+ * Common device: a single screen of portrait/landscape orientation
+ */
+ private static final List<Point> STANDARD_DISPLAY = List.of(PORTRAIT_ONE);
+
+ /** 1: folded: portrait, unfolded: square with w < h */
+ private static final List<Point> FOLDABLE_ONE = List.of(PORTRAIT_ONE, SQUARE_PORTRAIT_ONE);
+
+ /** 2: folded: portrait, unfolded: square with w > h */
+ private static final List<Point> FOLDABLE_TWO = List.of(PORTRAIT_TWO, SQUARE_LANDSCAPE_ONE);
+
+ /** 3: folded: square with w < h, unfolded: portrait */
+ private static final List<Point> FOLDABLE_THREE = List.of(SQUARE_PORTRAIT_ONE, PORTRAIT_THREE);
+
+ /** 4: folded: square with w > h, unfolded: portrait */
+ private static final List<Point> FOLDABLE_FOUR = List.of(SQUARE_LANDSCAPE_ONE, PORTRAIT_FOUR);
+
+ /**
+ * List of different sets of displays for foldable devices. Foldable devices have two displays:
+ * a folded (smaller) unfolded (larger).
+ */
+ private static final List<List<Point>> ALL_FOLDABLE_DISPLAYS = List.of(
+ FOLDABLE_ONE, FOLDABLE_TWO, FOLDABLE_THREE, FOLDABLE_FOUR);
+
+ private SparseArray<Point> mDisplaySizes = new SparseArray<>();
+ private int mFolded = ORIENTATION_UNKNOWN;
+ private int mFoldedRotated = ORIENTATION_UNKNOWN;
+ private int mUnfolded = ORIENTATION_UNKNOWN;
+ private int mUnfoldedRotated = ORIENTATION_UNKNOWN;
+
+ private static final List<Integer> ALL_MODES = List.of(
+ WallpaperCropper.ADD, WallpaperCropper.REMOVE, WallpaperCropper.BALANCE);
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
+ }
+
+ private void setUpWithDisplays(List<Point> displaySizes) {
+ mDisplaySizes = new SparseArray<>();
+ displaySizes.forEach(size -> {
+ mDisplaySizes.put(getOrientation(size), size);
+ Point rotated = new Point(size.y, size.x);
+ mDisplaySizes.put(getOrientation(rotated), rotated);
+ });
+ when(mWallpaperDisplayHelper.getDefaultDisplaySizes()).thenReturn(mDisplaySizes);
+ if (displaySizes.size() == 2) {
+ Point largestDisplay = displaySizes.stream().max(
+ Comparator.comparingInt(p -> p.x * p.y)).get();
+ Point smallestDisplay = displaySizes.stream().min(
+ Comparator.comparingInt(p -> p.x * p.y)).get();
+ mUnfolded = getOrientation(largestDisplay);
+ mFolded = getOrientation(smallestDisplay);
+ mUnfoldedRotated = getRotatedOrientation(mUnfolded);
+ mFoldedRotated = getRotatedOrientation(mFolded);
+ }
+ doAnswer(invocation -> getFoldedOrientation(invocation.getArgument(0)))
+ .when(mWallpaperDisplayHelper).getFoldedOrientation(anyInt());
+ doAnswer(invocation -> getUnfoldedOrientation(invocation.getArgument(0)))
+ .when(mWallpaperDisplayHelper).getUnfoldedOrientation(anyInt());
+ }
+
+ private int getFoldedOrientation(int orientation) {
+ if (orientation == ORIENTATION_UNKNOWN) return ORIENTATION_UNKNOWN;
+ if (orientation == mUnfolded) return mFolded;
+ if (orientation == mUnfoldedRotated) return mFoldedRotated;
+ return ORIENTATION_UNKNOWN;
+ }
+
+ private int getUnfoldedOrientation(int orientation) {
+ if (orientation == ORIENTATION_UNKNOWN) return ORIENTATION_UNKNOWN;
+ if (orientation == mFolded) return mUnfolded;
+ if (orientation == mFoldedRotated) return mUnfoldedRotated;
+ return ORIENTATION_UNKNOWN;
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#noParallax} successfully removes the parallax in a simple
+ * case, removing the right or left part depending on the "rtl" argument.
+ */
+ @Test
+ public void testNoParallax_noScale() {
+ Point displaySize = new Point(1000, 1000);
+ Point bitmapSize = new Point(1200, 1000);
+ Point expectedCropSize = new Point(1000, 1000);
+ Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ false))
+ .isEqualTo(leftOf(crop, expectedCropSize));
+ assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ true))
+ .isEqualTo(rightOf(crop, expectedCropSize));
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#noParallax} correctly takes zooming into account.
+ */
+ @Test
+ public void testNoParallax_withScale() {
+ Point displaySize = new Point(1000, 1000);
+ Point bitmapSize = new Point(600, 500);
+ Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ Point expectedCropSize = new Point(500, 500);
+ assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ false))
+ .isEqualTo(leftOf(crop, expectedCropSize));
+ assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ true))
+ .isEqualTo(rightOf(crop, expectedCropSize));
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#noParallax} correctly removes parallax when the image is
+ * cropped, i.e. when the crop rectangle is not the full bitmap.
+ */
+ @Test
+ public void testNoParallax_withScaleAndCrop() {
+ Point displaySize = new Point(1000, 1000);
+ Point bitmapSize = new Point(2000, 2000);
+ Rect crop = new Rect(300, 1000, 900, 1500);
+ Point expectedCropSize = new Point(500, 500);
+ assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ false))
+ .isEqualTo(leftOf(crop, expectedCropSize));
+ assertThat(WallpaperCropper.noParallax(crop, displaySize, bitmapSize, /* rtl */ true))
+ .isEqualTo(rightOf(crop, expectedCropSize));
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getAdjustedCrop} does nothing when the crop has the same
+ * width/height ratio than the screen.
+ */
+ @Test
+ public void testGetAdjustedCrop_noOp() {
+ Point displaySize = new Point(1000, 1000);
+
+ for (Point bitmapSize: List.of(
+ new Point(1000, 1000),
+ new Point(2000, 2000),
+ new Point(500, 500))) {
+ for (Rect crop: List.of(
+ new Rect(0, 0, bitmapSize.x, bitmapSize.y),
+ new Rect(100, 200, bitmapSize.x - 100, bitmapSize.y))) {
+ for (int mode: ALL_MODES) {
+ for (boolean rtl: List.of(true, false)) {
+ for (boolean parallax: List.of(true, false)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, parallax, rtl, mode))
+ .isEqualTo(crop);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with parallax = true,
+ * does not keep more width than needed for {@link WallpaperCropper#MAX_PARALLAX}.
+ */
+ @Test
+ public void testGetAdjustedCrop_tooMuchParallax() {
+ Point displaySize = new Point(1000, 1000);
+ int tooLargeWidth = (int) (displaySize.x * (1 + 2 * WallpaperCropper.MAX_PARALLAX));
+ Point bitmapSize = new Point(tooLargeWidth, 1000);
+ Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ int expectedWidth = (int) (displaySize.x * (1 + WallpaperCropper.MAX_PARALLAX));
+ Point expectedCropSize = new Point(expectedWidth, 1000);
+ for (int mode: ALL_MODES) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, true, false, mode))
+ .isEqualTo(leftOf(crop, expectedCropSize));
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, true, true, mode))
+ .isEqualTo(rightOf(crop, expectedCropSize));
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with parallax = true,
+ * does not remove parallax if the parallax is below {@link WallpaperCropper#MAX_PARALLAX}.
+ */
+ @Test
+ public void testGetAdjustedCrop_acceptableParallax() {
+ Point displaySize = new Point(1000, 1000);
+ List<Integer> acceptableWidths = List.of(displaySize.x,
+ (int) (displaySize.x * (1 + 0.5 * WallpaperCropper.MAX_PARALLAX)),
+ (int) (displaySize.x * (1 + 0.9 * WallpaperCropper.MAX_PARALLAX)),
+ (int) (displaySize.x * (1 + 1.0 * WallpaperCropper.MAX_PARALLAX)));
+ for (int acceptableWidth: acceptableWidths) {
+ Point bitmapSize = new Point(acceptableWidth, 1000);
+ Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ for (int mode : ALL_MODES) {
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, true, rtl, mode))
+ .isEqualTo(crop);
+ }
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with
+ * {@link WallpaperCropper#ADD}, correctly enlarges the crop to match the display dimensions,
+ * and adds content to the crop by an equal amount on both sides when possible.
+ */
+ @Test
+ public void testGetAdjustedCrop_add() {
+ Point displaySize = new Point(1000, 1000);
+ Point bitmapSize = new Point(1000, 1000);
+
+ List<Rect> crops = List.of(
+ new Rect(0, 0, 900, 1000),
+ new Rect(0, 0, 1000, 900),
+ new Rect(0, 0, 400, 500),
+ new Rect(500, 600, 1000, 1000));
+
+ List<Rect> expectedAdjustedCrops = List.of(
+ new Rect(0, 0, 1000, 1000),
+ new Rect(0, 0, 1000, 1000),
+ new Rect(0, 0, 500, 500),
+ new Rect(500, 500, 1000, 1000));
+
+ for (int i = 0; i < crops.size(); i++) {
+ Rect crop = crops.get(i);
+ Rect expectedCrop = expectedAdjustedCrops.get(i);
+ for (boolean rtl: List.of(false, true)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.ADD))
+ .isEqualTo(expectedCrop);
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with
+ * {@link WallpaperCropper#REMOVE}, correctly shrinks the crop to match the display dimensions,
+ * and removes content by an equal amount on both sides.
+ */
+ @Test
+ public void testGetAdjustedCrop_remove() {
+ Point displaySize = new Point(1000, 1000);
+ Point bitmapSize = new Point(1500, 1500);
+
+ List<Rect> crops = List.of(
+ new Rect(50, 0, 1150, 1000),
+ new Rect(0, 50, 1000, 1150));
+
+ Point expectedCropSize = new Point(1000, 1000);
+
+ for (Rect crop: crops) {
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.REMOVE))
+ .isEqualTo(centerOf(crop, expectedCropSize));
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getAdjustedCrop}, when called with
+ * {@link WallpaperCropper#BALANCE}, gives an adjusted crop with the same center and same number
+ * of pixels when possible.
+ */
+ @Test
+ public void testGetAdjustedCrop_balance() {
+ Point displaySize = new Point(500, 1000);
+ Point transposedDisplaySize = new Point(1000, 500);
+ Point bitmapSize = new Point(1000, 1000);
+
+ List<Rect> crops = List.of(
+ new Rect(0, 250, 1000, 750),
+ new Rect(100, 0, 300, 100));
+
+ List<Rect> expectedAdjustedCrops = List.of(
+ new Rect(250, 0, 750, 1000),
+ new Rect(150, 0, 250, 200));
+
+ for (int i = 0; i < crops.size(); i++) {
+ Rect crop = crops.get(i);
+ Rect expected = expectedAdjustedCrops.get(i);
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, false, false, WallpaperCropper.BALANCE))
+ .isEqualTo(expected);
+
+ Rect transposedCrop = new Rect(crop.top, crop.left, crop.bottom, crop.right);
+ Rect expectedTransposed = new Rect(
+ expected.top, expected.left, expected.bottom, expected.right);
+ assertThat(WallpaperCropper.getAdjustedCrop(transposedCrop, bitmapSize,
+ transposedDisplaySize, false, false, WallpaperCropper.BALANCE))
+ .isEqualTo(expectedTransposed);
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getCrop} follows a simple centre-align strategy when
+ * no suggested crops are provided.
+ */
+ @Test
+ public void testGetCrop_noSuggestedCrops_centersWallpaper() {
+ setUpWithDisplays(STANDARD_DISPLAY);
+ Point bitmapSize = new Point(800, 1000);
+ Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ SparseArray<Rect> suggestedCrops = new SparseArray<>();
+
+ List<Point> displaySizes = List.of(
+ new Point(500, 1000),
+ new Point(1000, 500));
+ List<Point> expectedCropSizes = List.of(
+ new Point(500, 1000),
+ new Point(800, 400));
+
+ for (int i = 0; i < displaySizes.size(); i++) {
+ Point displaySize = displaySizes.get(i);
+ Point expectedCropSize = expectedCropSizes.get(i);
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(mWallpaperCropper.getCrop(
+ displaySize, bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(centerOf(bitmapRect, expectedCropSize));
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getCrop} reuses a suggested crop of the same orientation
+ * as the display if possible, and does not remove additional width for parallax,
+ * but adds width if necessary.
+ */
+ @Test
+ public void testGetCrop_hasSuggestedCrop() {
+ setUpWithDisplays(STANDARD_DISPLAY);
+ Point bitmapSize = new Point(800, 1000);
+ SparseArray<Rect> suggestedCrops = new SparseArray<>();
+ suggestedCrops.put(PORTRAIT, new Rect(0, 0, 400, 800));
+ for (int otherOrientation: List.of(LANDSCAPE, SQUARE_LANDSCAPE, SQUARE_PORTRAIT)) {
+ suggestedCrops.put(otherOrientation, new Rect(0, 0, 10, 10));
+ }
+
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(mWallpaperCropper.getCrop(
+ new Point(300, 800), bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(suggestedCrops.get(PORTRAIT));
+ assertThat(mWallpaperCropper.getCrop(
+ new Point(500, 800), bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(new Rect(0, 0, 500, 800));
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getCrop}, if there is no suggested crop of the same
+ * orientation as the display, reuses a suggested crop of the rotated orientation if possible,
+ * and preserves the center and number of pixels of the crop if possible.
+ * <p>
+ * To simplify, in this test case all crops have the same size as the display (no zoom)
+ * and are at the center of the image. Also the image is large enough to preserver the number
+ * of pixels (no additional zoom required).
+ */
+ @Test
+ public void testGetCrop_hasRotatedSuggestedCrop() {
+ setUpWithDisplays(STANDARD_DISPLAY);
+ Point bitmapSize = new Point(2000, 1800);
+ Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+ SparseArray<Rect> suggestedCrops = new SparseArray<>();
+ Point portrait = PORTRAIT_ONE;
+ Point landscape = new Point(PORTRAIT_ONE.y, PORTRAIT_ONE.x);
+ Point squarePortrait = SQUARE_PORTRAIT_ONE;
+ Point squareLandscape = new Point(SQUARE_PORTRAIT_ONE.y, SQUARE_PORTRAIT_ONE.y);
+ suggestedCrops.put(PORTRAIT, centerOf(bitmapRect, portrait));
+ suggestedCrops.put(SQUARE_LANDSCAPE, centerOf(bitmapRect, squareLandscape));
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(mWallpaperCropper.getCrop(
+ landscape, bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(centerOf(bitmapRect, landscape));
+
+ assertThat(mWallpaperCropper.getCrop(
+ squarePortrait, bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(centerOf(bitmapRect, squarePortrait));
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getCrop}, when asked for a folded crop with a suggested
+ * crop only for the relative unfolded orientation, creates the folded crop at the center of the
+ * unfolded crop, by removing content on two sides to match the folded screen dimensions.
+ * <p>
+ * To simplify, in this test case all crops have the same size as the display (no zoom)
+ * and are at the center of the image.
+ */
+ @Test
+ public void testGetCrop_hasUnfoldedSuggestedCrop() {
+ for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
+ setUpWithDisplays(displaySizes);
+ Point bitmapSize = new Point(2000, 2400);
+ Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+
+ Point largestDisplay = displaySizes.stream().max(
+ Comparator.comparingInt(a -> a.x * a.y)).orElseThrow();
+ int unfoldedOne = getOrientation(largestDisplay);
+ int unfoldedTwo = getRotatedOrientation(unfoldedOne);
+ Rect unfoldedCropOne = centerOf(bitmapRect, mDisplaySizes.get(unfoldedOne));
+ Rect unfoldedCropTwo = centerOf(bitmapRect, mDisplaySizes.get(unfoldedTwo));
+ SparseArray<Rect> suggestedCrops = new SparseArray<>();
+ suggestedCrops.put(unfoldedOne, unfoldedCropOne);
+ suggestedCrops.put(unfoldedTwo, unfoldedCropTwo);
+
+ int foldedOne = getFoldedOrientation(unfoldedOne);
+ int foldedTwo = getFoldedOrientation(unfoldedTwo);
+ Point foldedDisplayOne = mDisplaySizes.get(foldedOne);
+ Point foldedDisplayTwo = mDisplaySizes.get(foldedTwo);
+
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(mWallpaperCropper.getCrop(
+ foldedDisplayOne, bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(centerOf(unfoldedCropOne, foldedDisplayOne));
+
+ assertThat(mWallpaperCropper.getCrop(
+ foldedDisplayTwo, bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(centerOf(unfoldedCropTwo, foldedDisplayTwo));
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getCrop}, when asked for an unfolded crop with a suggested
+ * crop only for the relative folded orientation, creates the unfolded crop with the same center
+ * as the folded crop, by adding content on two sides to match the unfolded screen dimensions.
+ * <p>
+ * To simplify, in this test case all crops have the same size as the display (no zoom) and are
+ * at the center of the image. Also the image is large enough to add content.
+ */
+ @Test
+ public void testGetCrop_hasFoldedSuggestedCrop() {
+ for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
+ setUpWithDisplays(displaySizes);
+ Point bitmapSize = new Point(2000, 2000);
+ Rect bitmapRect = new Rect(0, 0, 2000, 2000);
+
+ Point smallestDisplay = displaySizes.stream().min(
+ Comparator.comparingInt(a -> a.x * a.y)).orElseThrow();
+ int foldedOne = getOrientation(smallestDisplay);
+ int foldedTwo = getRotatedOrientation(foldedOne);
+ Point foldedDisplayOne = mDisplaySizes.get(foldedOne);
+ Point foldedDisplayTwo = mDisplaySizes.get(foldedTwo);
+ Rect foldedCropOne = centerOf(bitmapRect, foldedDisplayOne);
+ Rect foldedCropTwo = centerOf(bitmapRect, foldedDisplayTwo);
+ SparseArray<Rect> suggestedCrops = new SparseArray<>();
+ suggestedCrops.put(foldedOne, foldedCropOne);
+ suggestedCrops.put(foldedTwo, foldedCropTwo);
+
+ int unfoldedOne = getUnfoldedOrientation(foldedOne);
+ int unfoldedTwo = getUnfoldedOrientation(foldedTwo);
+ Point unfoldedDisplayOne = mDisplaySizes.get(unfoldedOne);
+ Point unfoldedDisplayTwo = mDisplaySizes.get(unfoldedTwo);
+
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(centerOf(mWallpaperCropper.getCrop(
+ unfoldedDisplayOne, bitmapSize, suggestedCrops, rtl), foldedDisplayOne))
+ .isEqualTo(foldedCropOne);
+
+ assertThat(centerOf(mWallpaperCropper.getCrop(
+ unfoldedDisplayTwo, bitmapSize, suggestedCrops, rtl), foldedDisplayTwo))
+ .isEqualTo(foldedCropTwo);
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getCrop}, when asked for an folded crop with a suggested
+ * crop only for the rotated unfolded orientation, creates the folded crop from that crop by
+ * combining a rotate + fold operation. The folded crop should have less pixels than the
+ * unfolded crop due to the fold operation which removes content on both sides of the image.
+ * <p>
+ * To simplify, in this test case all crops have the same size as the display (no zoom)
+ * and are at the center of the image.
+ */
+ @Test
+ public void testGetCrop_hasRotatedUnfoldedSuggestedCrop() {
+ for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
+ setUpWithDisplays(displaySizes);
+ Point bitmapSize = new Point(2000, 2000);
+ Rect bitmapRect = new Rect(0, 0, 2000, 2000);
+ Point largestDisplay = displaySizes.stream().max(
+ Comparator.comparingInt(a -> a.x * a.y)).orElseThrow();
+ int unfoldedOne = getOrientation(largestDisplay);
+ int unfoldedTwo = getRotatedOrientation(unfoldedOne);
+ for (int unfolded: List.of(unfoldedOne, unfoldedTwo)) {
+ Rect unfoldedCrop = centerOf(bitmapRect, mDisplaySizes.get(unfolded));
+ int rotatedUnfolded = getRotatedOrientation(unfolded);
+ Rect rotatedUnfoldedCrop = centerOf(bitmapRect, mDisplaySizes.get(rotatedUnfolded));
+ SparseArray<Rect> suggestedCrops = new SparseArray<>();
+ suggestedCrops.put(unfolded, unfoldedCrop);
+ int rotatedFolded = getFoldedOrientation(rotatedUnfolded);
+ Point rotatedFoldedDisplay = mDisplaySizes.get(rotatedFolded);
+
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(mWallpaperCropper.getCrop(
+ rotatedFoldedDisplay, bitmapSize, suggestedCrops, rtl))
+ .isEqualTo(centerOf(rotatedUnfoldedCrop, rotatedFoldedDisplay));
+ }
+ }
+ }
+ }
+
+ /**
+ * Test that {@link WallpaperCropper#getCrop}, when asked for an unfolded crop with a suggested
+ * crop only for the rotated folded orientation, creates the unfolded crop from that crop by
+ * combining a rotate + unfold operation. The unfolded crop should have more pixels than the
+ * folded crop due to the unfold operation which adds content on two sides of the image.
+ * <p>
+ * To simplify, in this test case all crops have the same size as the display (no zoom)
+ * and are centered inside the image. Also the image is large enough to add content.
+ */
+ @Test
+ public void testGetCrop_hasRotatedFoldedSuggestedCrop() {
+ for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
+ setUpWithDisplays(displaySizes);
+ Point bitmapSize = new Point(2000, 2000);
+ Rect bitmapRect = new Rect(0, 0, 2000, 2000);
+
+ Point smallestDisplay = displaySizes.stream().min(
+ Comparator.comparingInt(a -> a.x * a.y)).orElseThrow();
+ int foldedOne = getOrientation(smallestDisplay);
+ int foldedTwo = getRotatedOrientation(foldedOne);
+ for (int folded: List.of(foldedOne, foldedTwo)) {
+ Rect foldedCrop = centerOf(bitmapRect, mDisplaySizes.get(folded));
+ SparseArray<Rect> suggestedCrops = new SparseArray<>();
+ suggestedCrops.put(folded, foldedCrop);
+ int rotatedFolded = getRotatedOrientation(folded);
+ int rotatedUnfolded = getUnfoldedOrientation(rotatedFolded);
+ Point rotatedFoldedDisplay = mDisplaySizes.get(rotatedFolded);
+ Rect rotatedFoldedCrop = centerOf(bitmapRect, rotatedFoldedDisplay);
+ Point rotatedUnfoldedDisplay = mDisplaySizes.get(rotatedUnfolded);
+
+ for (boolean rtl : List.of(false, true)) {
+ Rect rotatedUnfoldedCrop = mWallpaperCropper.getCrop(
+ rotatedUnfoldedDisplay, bitmapSize, suggestedCrops, rtl);
+ assertThat(centerOf(rotatedUnfoldedCrop, rotatedFoldedDisplay))
+ .isEqualTo(rotatedFoldedCrop);
+ }
+ }
+ }
+ }
+
+ private static Rect centerOf(Rect container, Point point) {
+ checkSubset(container, point);
+ int diffWidth = container.width() - point.x;
+ int diffHeight = container.height() - point.y;
+ int startX = container.left + diffWidth / 2;
+ int startY = container.top + diffHeight / 2;
+ return new Rect(startX, startY, startX + point.x, startY + point.y);
+ }
+
+ private static Rect leftOf(Rect container, Point point) {
+ Rect result = centerOf(container, point);
+ result.offset(container.left - result.left, 0);
+ return result;
+ }
+
+ private static Rect rightOf(Rect container, Point point) {
+ checkSubset(container, point);
+ Rect result = centerOf(container, point);
+ result.offset(container.right - result.right, 0);
+ return result;
+ }
+
+ private static void checkSubset(Rect container, Point point) {
+ if (container.width() < point.x || container.height() < point.y) {
+ throw new IllegalArgumentException();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
index d367f71de921..4a8fe416e0dd 100644
--- a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
@@ -19,7 +19,8 @@ package com.android.server;
import android.os.ServiceManager;
import android.os.image.IDynamicSystemService;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
public class DynamicSystemServiceTest extends AndroidTestCase {
private static final String TAG = "DynamicSystemServiceTests";
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
index 4db27d272f8e..dc26e6e2374c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
@@ -17,9 +17,9 @@
package com.android.server.accessibility;
import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT;
-import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId;
-import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges;
-import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId;
+import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.DisplayIdMatcher.displayId;
+import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowChangesMatcher.a11yWindowChanges;
+import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowIdMatcher.a11yWindowId;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -27,11 +27,13 @@ import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -42,6 +44,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.graphics.Point;
import android.graphics.Region;
import android.os.IBinder;
import android.os.LocaleList;
@@ -63,6 +66,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.test.MessageCapturingHandler;
+import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
@@ -117,9 +121,8 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
// List of window token, mapping from windowId -> window token.
private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>();
- // List of window info lists, mapping from displayId -> window info lists.
- private final SparseArray<ArrayList<WindowInfo>> mWindowInfos =
- new SparseArray<>();
+ // List of window info lists, mapping from displayId -> a11y window lists.
+ private final SparseArray<ArrayList<AccessibilityWindow>> mWindows = new SparseArray<>();
// List of callback, mapping from displayId -> callback.
private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows =
new SparseArray<>();
@@ -129,6 +132,13 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
+ // This maps displayId -> next region offset.
+ // Touchable region must have un-occluded area so that it's exposed to a11y services.
+ // This offset can be used as left and top of new region so that top-left of each region are
+ // kept visible.
+ // It's expected to be incremented by some amount everytime the value is used.
+ private final SparseArray<Integer> mNextRegionOffsets = new SparseArray<>();
+
@Mock
private WindowManagerInternal mMockWindowManagerInternal;
@Mock
@@ -162,7 +172,7 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
doAnswer((invocation) -> {
- onWindowsForAccessibilityChanged(invocation.getArgument(0), false);
+ onAccessibilityWindowsChanged(invocation.getArgument(0), false);
return null;
}).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt());
@@ -176,7 +186,7 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
// as top focused display before each testing starts.
startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
- // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
+ // AccessibilityEventSender is invoked during onAccessibilityWindowsChanged.
// Resets it for mockito verify of further test case.
Mockito.reset(mMockA11yEventSender);
@@ -240,19 +250,18 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
- WindowInfo focusedWindowInfo =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
+ final WindowInfo focusedWindowInfo =
+ mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo();
assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, focusedWindowInfo.token));
focusedWindowInfo.focused = false;
- focusedWindowInfo =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1);
- focusedWindowInfo.focused = true;
+ mWindows.get(Display.DEFAULT_DISPLAY).get(
+ DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().focused = true;
mA11yWindowManager.onTouchInteractionStart();
setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
}
@@ -276,7 +285,7 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
- onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
// The active window should not be changed.
assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
// The top focused window should not be changed.
@@ -304,8 +313,8 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
// The active window should be changed.
assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
// The top focused window should be changed.
@@ -315,53 +324,43 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void onWindowsChanged_shouldReportCorrectLayer() {
- // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+ // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup.
List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
for (int i = 0; i < a11yWindows.size(); i++) {
final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
- assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1,
+ assertThat(mWindows.get(Display.DEFAULT_DISPLAY).size() - i - 1,
is(a11yWindow.getLayer()));
}
}
@Test
public void onWindowsChanged_shouldReportCorrectOrder() {
- // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
+ // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup.
List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
for (int i = 0; i < a11yWindows.size(); i++) {
final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
final IBinder windowToken = mA11yWindowManager
.getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId());
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
+ final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY)
+ .get(i).getWindowInfo();
assertThat(windowToken, is(windowInfo.token));
}
}
@Test
public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- final int correctLayer =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
- windowInfo.layer += 1;
+ assertNotEquals("new title",
+ toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)
+ .get(0).getTitle()));
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
- assertNotEquals(correctLayer,
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
- }
+ mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().title = "new title";
- @Test
- public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() {
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- final int correctLayer =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
- windowInfo.layer += 1;
-
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
- assertEquals(correctLayer,
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ assertEquals("new title",
+ toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)
+ .get(0).getTitle()));
}
@Test
@@ -371,14 +370,10 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0);
final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
true, USER_SYSTEM_ID);
- final WindowInfo windowInfo = WindowInfo.obtain();
- windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
- windowInfo.token = token.asBinder();
- windowInfo.layer = 0;
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo);
+ mWindows.get(Display.DEFAULT_DISPLAY).set(0,
+ createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY));
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertNotEquals(oldWindow,
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0));
}
@@ -386,12 +381,12 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() {
final WindowInfo focusedWindowInfo =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX);
- final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
+ mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo();
+ final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo();
focusedWindowInfo.focused = false;
windowInfo.focused = true;
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)
.isFocused());
}
@@ -500,15 +495,18 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
// Updates top 2 z-order WindowInfo are whole visible.
- WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
- windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1);
- windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2,
- SCREEN_WIDTH, SCREEN_HEIGHT);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2));
+ final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
+ setRegionForMockAccessibilityWindow(secondWindow,
+ new Region(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
int windowId = a11yWindows.get(0).getId();
@@ -526,12 +524,17 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() {
// Updates z-order #1 WindowInfo is half visible.
- WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
-
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2));
+ final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
+ setRegionForMockAccessibilityWindow(secondWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
int windowId = a11yWindows.get(1).getId();
@@ -542,9 +545,17 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() {
- // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
+ // z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ // Note that the second window is also exposed even if region is empty because it's focused.
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
int windowId = a11yWindows.get(1).getId();
@@ -555,16 +566,21 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() {
// Updates z-order #0 WindowInfo to have two interact-able areas.
- Region region = new Region(0, 0, SCREEN_WIDTH, 200);
+ final Region region = new Region(0, 0, SCREEN_WIDTH, 200);
region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION);
- WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
- windowInfo.regionInScreen.set(region);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0);
+ setRegionForMockAccessibilityWindow(firstWindow, region);
+ final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1);
+ setRegionForMockAccessibilityWindow(secondWindow,
+ new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
final List<AccessibilityWindowInfo> a11yWindows =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
+ assertThat(a11yWindows, hasSize(2));
final Region outBounds = new Region();
- int windowId = a11yWindows.get(1).getId();
+ final int windowId = a11yWindows.get(1).getId();
mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds);
assertFalse(outBounds.getBounds().isEmpty());
@@ -575,7 +591,8 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() {
final IBinder eventWindowToken =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token;
+ mWindows.get(Display.DEFAULT_DISPLAY)
+ .get(DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().token;
final int eventWindowId = mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, eventWindowToken);
when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
@@ -766,7 +783,8 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus()
throws RemoteException {
final IBinder defaultFocusWinToken =
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token;
+ mWindows.get(Display.DEFAULT_DISPLAY).get(
+ DEFAULT_FOCUSED_INDEX).getWindowInfo().token;
final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, defaultFocusWinToken);
when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates())
@@ -811,8 +829,8 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void getPictureInPictureWindow_shouldNotNull() {
assertNull(mA11yWindowManager.getPictureInPictureWindowLocked());
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true;
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ mWindows.get(Display.DEFAULT_DISPLAY).get(1).getWindowInfo().inPictureInPicture = true;
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked());
}
@@ -826,8 +844,9 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
final IAccessibilityInteractionConnection mockRemoteConnection =
mA11yWindowManager.getConnectionLocked(
USER_SYSTEM_ID, outsideWindowId).getRemote();
- mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true;
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+ mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().hasFlagWatchOutsideTouch =
+ true;
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId);
verify(mockRemoteConnection).notifyOutsideTouch();
@@ -945,18 +964,14 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void sendAccessibilityEventOnWindowRemoval() {
- final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+ final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
// Removing index 0 because it's not focused, and avoids unnecessary layer change.
final int windowId =
getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- infos.remove(0);
- for (WindowInfo info : infos) {
- // Adjust layer number because it should start from 0.
- info.layer--;
- }
+ windows.remove(0);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
final ArgumentCaptor<AccessibilityEvent> captor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
@@ -970,21 +985,15 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void sendAccessibilityEventOnWindowAddition() throws RemoteException {
- final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
-
- for (WindowInfo info : infos) {
- // Adjust layer number because new window will have 0 so that layer number in
- // A11yWindowInfo in window won't be changed.
- info.layer++;
- }
+ final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
false, USER_SYSTEM_ID);
- addWindowInfo(infos, token, 0);
- final int windowId =
- getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1);
+ // Adding window to the front so that other windows' layer won't change.
+ windows.add(0, createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY));
+ final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
final ArgumentCaptor<AccessibilityEvent> captor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
@@ -998,11 +1007,11 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
@Test
public void sendAccessibilityEventOnWindowChange() {
- final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
- infos.get(0).title = "new title";
+ final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY);
+ windows.get(0).getWindowInfo().title = "new title";
final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
- onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+ onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
final ArgumentCaptor<AccessibilityEvent> captor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
@@ -1020,42 +1029,41 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
}
private void startTrackingPerDisplay(int displayId) throws RemoteException {
- ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
+ ArrayList<AccessibilityWindow> windowsForDisplay = new ArrayList<>();
// Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
// mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos
// for the test.
- int layer = 0;
for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
final IWindow token = addAccessibilityInteractionConnection(displayId,
true, USER_SYSTEM_ID);
- addWindowInfo(windowInfosForDisplay, token, layer++);
+ windowsForDisplay.add(createMockAccessibilityWindow(token, displayId));
}
for (int i = 0; i < NUM_APP_WINDOWS; i++) {
final IWindow token = addAccessibilityInteractionConnection(displayId,
false, USER_SYSTEM_ID);
- addWindowInfo(windowInfosForDisplay, token, layer++);
+ windowsForDisplay.add(createMockAccessibilityWindow(token, displayId));
}
// Sets up current focused window of display.
// Each display has its own current focused window if config_perDisplayFocusEnabled is true.
// Otherwise only default display needs to current focused window.
if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) {
- windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
+ windowsForDisplay.get(DEFAULT_FOCUSED_INDEX).getWindowInfo().focused = true;
}
// Turns on windows tracking, and update window info.
mA11yWindowManager.startTrackingWindows(displayId, false);
// Puts window lists into array.
- mWindowInfos.put(displayId, windowInfosForDisplay);
+ mWindows.put(displayId, windowsForDisplay);
// Sets the default display is the top focused display and
// its current focused window is the top focused window.
if (displayId == Display.DEFAULT_DISPLAY) {
setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX);
}
// Invokes callback for sending window lists to A11y framework.
- onWindowsForAccessibilityChanged(displayId, FORCE_SEND);
+ onAccessibilityWindowsChanged(displayId, FORCE_SEND);
assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(),
- windowInfosForDisplay.size());
+ windowsForDisplay.size());
}
private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) {
@@ -1109,36 +1117,28 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
return windowId;
}
- private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) {
- final WindowInfo windowInfo = WindowInfo.obtain();
- windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
- windowInfo.token = windowToken.asBinder();
- windowInfo.layer = layer;
- windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- windowInfos.add(windowInfo);
- }
-
private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) {
- final IBinder windowToken = mWindowInfos.get(displayId).get(index).token;
+ final IBinder windowToken = mWindows.get(displayId).get(index).getWindowInfo().token;
return mA11yWindowManager.findWindowIdLocked(
USER_SYSTEM_ID, windowToken);
}
private void setTopFocusedWindowAndDisplay(int displayId, int index) {
// Sets the top focus window.
- mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token;
+ mTopFocusedWindowToken = mWindows.get(displayId).get(index).getWindowInfo().token;
// Sets the top focused display.
mTopFocusedDisplayId = displayId;
}
- private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) {
+ private void onAccessibilityWindowsChanged(int displayId, boolean forceSend) {
WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId);
if (callbacks == null) {
callbacks = getWindowsForAccessibilityCallbacks(displayId);
mCallbackOfWindows.put(displayId, callbacks);
}
- callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId,
- mTopFocusedWindowToken, mWindowInfos.get(displayId));
+ callbacks.onAccessibilityWindowsChanged(forceSend, mTopFocusedDisplayId,
+ mTopFocusedWindowToken, new Point(SCREEN_WIDTH, SCREEN_HEIGHT),
+ mWindows.get(displayId));
}
private void changeFocusedWindowOnDisplayPerDisplayFocusConfig(
@@ -1147,23 +1147,23 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
if (mSupportPerDisplayFocus) {
// Gets the old focused window of display which wants to change focused window.
WindowInfo focusedWindowInfo =
- mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex);
+ mWindows.get(changeFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo();
// Resets the focus of old focused window.
focusedWindowInfo.focused = false;
// Gets the new window of display which wants to change focused window.
focusedWindowInfo =
- mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+ mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo();
// Sets the focus of new focused window.
focusedWindowInfo.focused = true;
} else {
// Gets the window of display which wants to change focused window.
WindowInfo focusedWindowInfo =
- mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+ mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo();
// Sets the focus of new focused window.
focusedWindowInfo.focused = true;
// Gets the old focused window of old top focused display.
focusedWindowInfo =
- mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex);
+ mWindows.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo();
// Resets the focus of old focused window.
focusedWindowInfo.focused = false;
// Changes the top focused display and window.
@@ -1171,6 +1171,42 @@ public class AccessibilityWindowManagerWithAccessibilityWindowTest {
}
}
+ private AccessibilityWindow createMockAccessibilityWindow(IWindow windowToken, int displayId) {
+ final WindowInfo windowInfo = WindowInfo.obtain();
+ // TODO(b/325341171): add tests with various kinds of windows such as
+ // changing window types, touchable or not, trusted or not, etc.
+ windowInfo.type = WindowManager.LayoutParams.TYPE_APPLICATION;
+ windowInfo.token = windowToken.asBinder();
+
+ final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class);
+ when(window.getWindowInfo()).thenReturn(windowInfo);
+ when(window.ignoreRecentsAnimationForAccessibility()).thenReturn(false);
+ when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused);
+ when(window.isTouchable()).thenReturn(true);
+ when(window.getType()).thenReturn(windowInfo.type);
+
+ setRegionForMockAccessibilityWindow(window, nextToucableRegion(displayId));
+ return window;
+ }
+
+ private void setRegionForMockAccessibilityWindow(AccessibilityWindow window, Region region) {
+ doAnswer(invocation -> {
+ ((Region) invocation.getArgument(0)).set(region);
+ return null;
+ }).when(window).getTouchableRegionInScreen(any(Region.class));
+ doAnswer(invocation -> {
+ ((Region) invocation.getArgument(0)).set(region);
+ return null;
+ }).when(window).getTouchableRegionInWindow(any(Region.class));
+ }
+
+ private Region nextToucableRegion(int displayId) {
+ final int topLeft = mNextRegionOffsets.get(displayId, 0);
+ final int bottomRight = topLeft + 100;
+ mNextRegionOffsets.put(displayId, topLeft + 10);
+ return new Region(topLeft, topLeft, bottomRight, bottomRight);
+ }
+
@Nullable
private static String toString(@Nullable CharSequence cs) {
return cs == null ? null : cs.toString();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
index aec3f451fac6..344e2c21f0a5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
@@ -103,7 +103,7 @@ public class BrailleDisplayConnectionTest {
}
BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
assertThat(scanner.getHidrawNodePaths(testDir.toPath()))
.containsExactly(hidrawNode0, hidrawNode1);
@@ -123,7 +123,7 @@ public class BrailleDisplayConnectionTest {
descriptor);
BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isEqualTo(descriptor);
}
@@ -133,7 +133,7 @@ public class BrailleDisplayConnectionTest {
when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(0);
BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isNull();
}
@@ -144,7 +144,7 @@ public class BrailleDisplayConnectionTest {
when(mNativeInterface.getHidrawUniq(anyInt())).thenReturn(macAddress);
BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
assertThat(scanner.getUniqueId(NULL_PATH)).isEqualTo(macAddress);
}
@@ -155,7 +155,7 @@ public class BrailleDisplayConnectionTest {
.thenReturn(BrailleDisplayConnection.BUS_USB);
BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
assertThat(scanner.getDeviceBusType(NULL_PATH))
.isEqualTo(BrailleDisplayConnection.BUS_USB);
@@ -167,7 +167,7 @@ public class BrailleDisplayConnectionTest {
.thenReturn(BrailleDisplayConnection.BUS_BLUETOOTH);
BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
assertThat(scanner.getDeviceBusType(NULL_PATH))
.isEqualTo(BrailleDisplayConnection.BUS_BLUETOOTH);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 8c0d44c46814..7fbd521b5432 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -189,6 +189,8 @@ public class FullScreenMagnificationGestureHandlerTest {
FullScreenMagnificationVibrationHelper mMockFullScreenMagnificationVibrationHelper;
@Mock
FullScreenMagnificationGestureHandler.MagnificationLogger mMockMagnificationLogger;
+ @Mock
+ OneFingerPanningSettingsProvider mMockOneFingerPanningSettingsProvider;
@Rule
public final TestableContext mContext = new TestableContext(getInstrumentation().getContext());
@@ -266,6 +268,7 @@ public class FullScreenMagnificationGestureHandlerTest {
mMgh.onDestroy();
mFullScreenMagnificationController.unregister(DISPLAY_0);
verify(mWindowMagnificationPromptController).onDestroy();
+ verify(mMockOneFingerPanningSettingsProvider).unregister();
Settings.Secure.putFloatForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
mOriginalMagnificationPersistedScale,
@@ -288,11 +291,10 @@ public class FullScreenMagnificationGestureHandlerTest {
DISPLAY_0,
mMockFullScreenMagnificationVibrationHelper,
mMockMagnificationLogger,
- ViewConfiguration.get(mContext));
+ ViewConfiguration.get(mContext),
+ mMockOneFingerPanningSettingsProvider);
if (isWatch()) {
- h.setSinglePanningEnabled(true);
- } else {
- h.setSinglePanningEnabled(false);
+ enableOneFingerPanning(true);
}
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
@@ -607,8 +609,8 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testTwoFingerTap_StateIsActivated_shouldInDelegating() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
- mMgh.setSinglePanningEnabled(false);
+ assumeTrue(isWatch());
+ enableOneFingerPanning(false);
goFromStateIdleTo(STATE_ACTIVATED);
allowEventDelegation();
@@ -623,8 +625,8 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testTwoFingerTap_StateIsIdle_shouldInDelegating() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
- mMgh.setSinglePanningEnabled(false);
+ assumeTrue(isWatch());
+ enableOneFingerPanning(false);
goFromStateIdleTo(STATE_IDLE);
allowEventDelegation();
@@ -830,7 +832,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
public void testActionUpNotAtEdge_singlePanningState_detectingState() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
send(upEvent());
@@ -841,8 +843,8 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
public void testScroll_SinglePanningDisabled_delegatingState() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
- mMgh.setSinglePanningEnabled(false);
+ assumeTrue(isWatch());
+ enableOneFingerPanning(false);
goFromStateIdleTo(STATE_ACTIVATED);
allowEventDelegation();
@@ -854,7 +856,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@FlakyTest
public void testScroll_singleHorizontalPanningAndAtEdge_leftEdgeOverscroll() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
@@ -878,7 +880,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@FlakyTest
public void testScroll_singleHorizontalPanningAndAtEdge_rightEdgeOverscroll() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
@@ -902,7 +904,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@FlakyTest
public void testScroll_singleVerticalPanningAndAtEdge_verticalOverscroll() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerX =
(INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f;
@@ -924,7 +926,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
public void testScroll_singlePanningAndAtEdge_noOverscroll() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
@@ -946,7 +948,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
public void testScroll_singleHorizontalPanningAndAtEdge_vibrate() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
mFullScreenMagnificationController.setCenter(
DISPLAY_0,
@@ -970,7 +972,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
public void testScroll_singleVerticalPanningAndAtEdge_doNotVibrate() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
mFullScreenMagnificationController.setCenter(
DISPLAY_0,
@@ -993,8 +995,9 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_FULLSCREEN_FLING_GESTURE)
public void singleFinger_testScrollAfterMagnified_startsFling() {
- assumeTrue(mMgh.mIsSinglePanningEnabled);
+ assumeTrue(isWatch());
goFromStateIdleTo(STATE_ACTIVATED);
swipeAndHold();
@@ -1274,6 +1277,10 @@ public class FullScreenMagnificationGestureHandlerTest {
mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
}
+ private void enableOneFingerPanning(boolean enable) {
+ when(mMockOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()).thenReturn(enable);
+ }
+
private void assertActionsInOrder(List<MotionEvent> actualEvents,
List<Integer> expectedActions) {
assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/OneFingerPanningSettingsProviderTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/OneFingerPanningSettingsProviderTest.java
new file mode 100644
index 000000000000..ac46ef9afa40
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/OneFingerPanningSettingsProviderTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.accessibility.magnification.OneFingerPanningSettingsProvider.State;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class OneFingerPanningSettingsProviderTest {
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(getInstrumentation().getContext());
+
+ private boolean mDefaultValue;
+ private boolean mOriginalIsOneFingerPanningEnabled;
+
+ private OneFingerPanningSettingsProvider mProvider;
+
+ @Before
+ public void setup() {
+ mDefaultValue = OneFingerPanningSettingsProvider.isOneFingerPanningEnabledDefault(mContext);
+ mOriginalIsOneFingerPanningEnabled = isSecureSettingsEnabled();
+ }
+
+ @After
+ public void tearDown() {
+ enableSecureSettings(mOriginalIsOneFingerPanningEnabled);
+ if (mProvider != null) {
+ mProvider.unregister();
+ }
+ }
+
+ @Test
+ public void isOneFingerPanningEnabled_flagDisabled_matchesDefault() {
+ mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ false);
+
+ assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(mDefaultValue);
+ }
+
+ @Test
+ public void isOneFingerPanningEnabled_flagEnabledSettingEnabled_true() {
+ enableSecureSettings(true);
+ mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true);
+
+ assertTrue(mProvider.isOneFingerPanningEnabled());
+ }
+
+ @Test
+ public void isOneFingerPanningEnabled_flagEnabledSettingDisabled_false() {
+ enableSecureSettings(false);
+ mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true);
+
+ assertFalse(mProvider.isOneFingerPanningEnabled());
+ }
+
+ @Test
+ public void isOneFingerPanningEnabled_flagEnabledSettingsFalse_false() {
+ mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true);
+
+ // Simulate observer triggered.
+ enableSecureSettings(false);
+ mProvider.mObserver.onChange(/* selfChange= */ false);
+
+ assertFalse(mProvider.isOneFingerPanningEnabled());
+ }
+
+ @Test
+ public void isOneFingerPanningEnabled_flagEnabledSettingsTrue_true() {
+ mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true);
+
+ // Simulate observer triggered.
+ enableSecureSettings(true);
+ mProvider.mObserver.onChange(/* selfChange= */ false);
+
+ assertTrue(mProvider.isOneFingerPanningEnabled());
+ }
+
+ @Test
+ public void isOneFingerPanningEnabled_flagDisabledSettingsChanges_valueUnchanged() {
+ mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ false);
+ var previousValue = mProvider.isOneFingerPanningEnabled();
+
+ enableSecureSettings(!previousValue);
+
+ assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(previousValue);
+ assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(mDefaultValue);
+ }
+
+ @Test
+ public void unregister_featureEnabled_contentResolverNull() {
+ var provider = new OneFingerPanningSettingsProvider(
+ mContext, /* featureFlagEnabled */ true);
+
+ provider.unregister();
+
+ assertThat(provider.mContentResolver).isNull();
+ }
+
+ @Test
+ public void unregister_featureDisabled_noError() {
+ var provider = new OneFingerPanningSettingsProvider(
+ mContext, /* featureFlagEnabled */ false);
+
+ provider.unregister();
+ }
+
+ private void enableSecureSettings(boolean enable) {
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ OneFingerPanningSettingsProvider.KEY,
+ enable ? State.ON : State.OFF,
+ mContext.getUserId());
+ }
+
+ private boolean isSecureSettingsEnabled() {
+ return State.ON == Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ OneFingerPanningSettingsProvider.KEY,
+ mDefaultValue ? State.ON : State.OFF,
+ mContext.getUserId());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index caa2e367f009..e0a99b016da9 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -70,9 +70,10 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import androidx.test.filters.SmallTest;
+
import com.android.server.LocalServices;
import org.mockito.ArgumentCaptor;
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index 29a920a0386f..61ac74cc3490 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -31,17 +30,16 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteStatement;
import android.os.Build;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java b/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java
index 08a65292cf20..1acb8acca71a 100644
--- a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java
@@ -24,6 +24,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM
import static com.android.server.adaptiveauth.AdaptiveAuthService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -32,9 +33,11 @@ import static org.mockito.Mockito.when;
import android.app.KeyguardManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.AuthenticationStateListener;
import android.hardware.biometrics.BiometricManager;
import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +65,7 @@ import org.mockito.MockitoAnnotations;
/**
* atest FrameworksServicesTests:AdaptiveAuthServiceTest
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AdaptiveAuthServiceTest {
@@ -103,6 +107,10 @@ public class AdaptiveAuthServiceTest {
mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
mContext = spy(ApplicationProvider.getApplicationContext());
+
+ assumeTrue("Adaptive auth is disabled on device",
+ !mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
+
when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mContext.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 99eb047e7e37..0bf419ec242c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -52,7 +52,6 @@ import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.server.wm.settings.SettingsSession;
-import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
import android.util.KeyValueListParser;
import android.util.Log;
@@ -60,6 +59,7 @@ import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
import androidx.test.uiautomator.UiDevice;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
index 4b359eb11d58..cfcb3dd56c29 100644
--- a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
@@ -38,10 +38,10 @@ import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.server.wm.settings.SettingsSession;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.SystemUtil;
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 9acc4bd19de2..8a7815e12c8b 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -47,11 +47,12 @@ import android.content.pm.ShortcutServiceInternal;
import android.os.Handler;
import android.os.UserHandle;
import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
import android.util.Xml;
import android.widget.RemoteViews;
+import androidx.test.filters.SmallTest;
+
import com.android.frameworks.servicestests.R;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.modules.utils.TypedXmlPullParser;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java
index bb0063427339..fa1fd90e10c9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/ALSProbeTest.java
@@ -344,6 +344,21 @@ public class ALSProbeTest {
verifyNoMoreInteractions(mSensorManager);
}
+ @Test
+ public void testAwaitLuxWhenNoLightSensor() {
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(null);
+ mProbe = new ALSProbe(mSensorManager, new Handler(mLooper.getLooper()), TIMEOUT_MS - 1);
+
+ AtomicInteger lux = new AtomicInteger(-5);
+ mProbe.awaitNextLux((v) -> lux.set(Math.round(v)), null /* handler */);
+
+ // Verify that no light sensor will be registered.
+ verify(mSensorManager, times(0)).registerListener(
+ mSensorEventListenerCaptor.capture(), any(), anyInt());
+
+ assertThat(lux.get()).isEqualTo(-1);
+ }
+
private void moveTimeBy(long millis) {
mLooper.moveTimeForward(millis);
mLooper.processAllMessages();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java
index 5b81277250c2..c12f13ee8cbe 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java
@@ -25,7 +25,8 @@ import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java
index 9d0c84edb366..5d4b04f22182 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java
@@ -28,9 +28,9 @@ import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContext;
+import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java
index 1b4c01723027..a1e43fbdf7b7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java
@@ -31,9 +31,9 @@ import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
index 8d74fd1d56be..9845b58f79bf 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
@@ -31,7 +31,8 @@ import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java
index dbbd69bdd3b5..d6bc73e9f3da 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java
@@ -28,7 +28,8 @@ import android.content.Context;
import android.hardware.biometrics.face.ISession;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java
index fb5502a1795d..e8cb5ad9b550 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java
@@ -25,7 +25,8 @@ import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java
index eb8cc9c63e75..b60c845106ec 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java
@@ -27,9 +27,9 @@ import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContext;
+import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
index b9a4fb4e0939..b5d73d256f7d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
@@ -16,8 +16,8 @@
package com.android.server.biometrics.sensors.face.hidl;
-import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC;
import static com.android.server.biometrics.sensors.face.hidl.FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC;
+import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC;
import static com.google.common.truth.Truth.assertThat;
@@ -45,9 +45,9 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.hardware.keymaster.Timestamp;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContext;
+import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.HardwareAuthTokenUtils;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java
index 840961938cb2..72b44d475bba 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java
@@ -25,7 +25,8 @@ import android.hardware.biometrics.fingerprint.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
index 723f916f99c8..b5df8362b09f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
@@ -31,7 +31,8 @@ import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
index d723e87a62ad..cdf1266d6a06 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
@@ -28,7 +28,8 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.hardware.keymaster.Timestamp;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
diff --git a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
index 01159b1f901c..bcb4877f64c7 100644
--- a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
@@ -32,7 +32,6 @@ import android.testing.AndroidTestingRunner;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.companion.PackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
index 034466383bac..28eee6644b60 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
@@ -36,9 +36,9 @@ import android.content.pm.UserProperties;
import android.os.Bundle;
import android.os.UserManager;
import android.provider.ContactsContract;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
import junit.framework.TestCase;
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index a694d5e37566..95f893bf2d17 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -21,7 +21,8 @@ import android.content.ContentResolver;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
/**
* Test for SyncOperation.
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
index 5fe60d779fa6..b012aaaed3bf 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -16,18 +16,26 @@
package com.android.server.contentprotection;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_ENABLED;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY;
+import static android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyManagerInternal;
-import android.content.ContentResolver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
-import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.TestableContentResolver;
import android.testing.TestableContext;
@@ -36,6 +44,9 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.LocalServices;
+
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,29 +89,36 @@ public class ContentProtectionConsentManagerTest {
public final TestableContext mTestableContext =
new TestableContext(ApplicationProvider.getApplicationContext());
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final TestableContentResolver mTestableContentResolver =
mTestableContext.getContentResolver();
- @Mock private ContentResolver mMockContentResolver;
-
@Mock private DevicePolicyManagerInternal mMockDevicePolicyManagerInternal;
+ @Mock private DevicePolicyCache mMockDevicePolicyCache;
+
+ @Before
+ public void setup() {
+ setupLocalService(DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+ }
+
@Test
- public void constructor_registersContentObserver() {
+ public void isConsentGranted_policyFlagDisabled_packageVerifierNotGranted() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
- createContentProtectionConsentManager(mMockContentResolver);
+ createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
- assertThat(manager.mContentObserver).isNotNull();
- verify(mMockContentResolver)
- .registerContentObserver(
- URI_PACKAGE_VERIFIER_USER_CONSENT,
- /* notifyForDescendants= */ false,
- manager.mContentObserver,
- UserHandle.USER_ALL);
+ assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_packageVerifierNotGranted() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierNotGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
@@ -108,10 +126,12 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_contentProtectionNotGranted() {
+ public void isConsentGranted_policyFlagDisabled_contentProtectionNotGranted() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
@@ -119,10 +139,52 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagDisabled_packageVerifierGranted_userNotManaged() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagDisabled_packageVerifierGranted_userManaged() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userNotManaged_contentProtectionNotGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_packageVerifierGranted_userNotManaged() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userNotManaged_contentProtectionGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
@@ -130,22 +192,110 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyDisabled() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_DISABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyEnabled() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
}
@Test
- public void isConsentGranted_packageVerifierGranted_userManaged() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyNotControlled_contentProtectionGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
.thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
boolean actual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyNotControlled_contentProtectionNotGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyNotControlled_contentProtectionDefault() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagDisabled_packageVerifierDefault() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_DEFAULT, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_packageVerifierDefault() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierDefault() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_DEFAULT, VALUE_TRUE);
@@ -153,10 +303,12 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_contentProtectionDefault() {
+ public void isConsentGranted_policyFlagDisabled_contentProtectionDefault() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
@@ -164,57 +316,108 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void contentObserver_packageVerifier() {
+ public void contentObserver_policyFlagDisabled_packageVerifier() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
- createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
+ createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
+
boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
- notifyContentObserver(
- manager,
- URI_PACKAGE_VERIFIER_USER_CONSENT,
- KEY_PACKAGE_VERIFIER_USER_CONSENT,
- VALUE_FALSE);
+ putGlobalSettings(KEY_PACKAGE_VERIFIER_USER_CONSENT, VALUE_TRUE);
boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
-
- assertThat(firstActual).isTrue();
assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
+
+ notifyContentObserver(manager, URI_PACKAGE_VERIFIER_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void contentObserver_contentProtection() {
+ public void contentObserver_policyFlagEnabled_packageVerifier() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
- createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
+ createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
+
boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
- notifyContentObserver(
- manager,
- URI_CONTENT_PROTECTION_USER_CONSENT,
- KEY_CONTENT_PROTECTION_USER_CONSENT,
- VALUE_FALSE);
+ putGlobalSettings(KEY_PACKAGE_VERIFIER_USER_CONSENT, VALUE_TRUE);
boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
- assertThat(firstActual).isTrue();
+ notifyContentObserver(manager, URI_PACKAGE_VERIFIER_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void contentObserver_policyFlagDisabled_contentProtection() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
+
+ putGlobalSettings(KEY_CONTENT_PROTECTION_USER_CONSENT, VALUE_TRUE);
+ boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
+
+ notifyContentObserver(manager, URI_CONTENT_PROTECTION_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void contentObserver_policyFlagEnabled_contentProtection() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ putGlobalSettings(KEY_CONTENT_PROTECTION_USER_CONSENT, VALUE_TRUE);
+ boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, times(2)).isUserOrganizationManaged(TEST_USER_ID);
+
+ notifyContentObserver(manager, URI_CONTENT_PROTECTION_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
+ verify(mMockDevicePolicyManagerInternal, times(3)).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
- private void notifyContentObserver(
- ContentProtectionConsentManager manager, Uri uri, String key, int value) {
+ private void putGlobalSettings(String key, int value) {
Settings.Global.putInt(mTestableContentResolver, key, value);
- // Observer has to be called manually, mTestableContentResolver is not propagating
- manager.mContentObserver.onChange(/* selfChange= */ false, uri, TEST_USER_ID);
}
- private ContentProtectionConsentManager createContentProtectionConsentManager(
- ContentResolver contentResolver) {
- return new ContentProtectionConsentManager(
- new Handler(Looper.getMainLooper()),
- contentResolver,
- mMockDevicePolicyManagerInternal);
+ private void notifyContentObserver(ContentProtectionConsentManager manager, Uri uri) {
+ // Observer has to be called manually, mTestableContentResolver is not propagating
+ manager.mContentObserver.onChange(/* selfChange= */ false, uri, TEST_USER_ID);
}
private ContentProtectionConsentManager createContentProtectionConsentManager(
@@ -227,6 +430,14 @@ public class ContentProtectionConsentManagerTest {
mTestableContentResolver,
KEY_CONTENT_PROTECTION_USER_CONSENT,
valueContentProtectionUserConsent);
- return createContentProtectionConsentManager(mTestableContentResolver);
+ return new ContentProtectionConsentManager(
+ new Handler(Looper.getMainLooper()),
+ mTestableContentResolver,
+ mMockDevicePolicyCache);
+ }
+
+ private <T> void setupLocalService(Class<T> clazz, T service) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, service);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
index 9660d6ba2f74..5f60ad93ec8b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
@@ -17,7 +17,7 @@ package com.android.server.devicepolicy;
import static com.google.common.truth.Truth.assertThat;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
index d55f3796f6cb..8a9538f2374a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
@@ -37,7 +37,8 @@ import android.os.Parcel;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.test.TestLooper;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 375b52d2d5e4..e81231ad3a93 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -25,8 +25,8 @@ import static com.google.common.truth.Truth.assertWithMessage;
import android.content.ComponentName;
import android.os.IpcDataCache;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index e59b5ea027ed..2ba3969bb9e5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -48,7 +48,6 @@ import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
-import android.security.KeyStore;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -102,7 +101,6 @@ public abstract class BaseLockSettingsServiceTests {
IActivityManager mActivityManager;
DevicePolicyManager mDevicePolicyManager;
DevicePolicyManagerInternal mDevicePolicyManagerInternal;
- KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
IAuthSecret mAuthSecretService;
WindowManagerInternal mMockWindowManager;
@@ -165,7 +163,6 @@ public abstract class BaseLockSettingsServiceTests {
new LockSettingsServiceTestable.MockInjector(
mContext,
mStorage,
- mKeyStore,
mActivityManager,
setUpStorageManagerMock(),
mSpManager,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 296d2cba83dd..f9077c4ae602 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -30,7 +30,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
-import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.service.gatekeeper.IGateKeeperService;
@@ -41,6 +40,7 @@ import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreMa
import com.android.server.pm.UserManagerInternal;
import java.io.FileNotFoundException;
+import java.security.KeyStore;
public class LockSettingsServiceTestable extends LockSettingsService {
private Intent mSavedFrpNotificationIntent = null;
@@ -50,7 +50,6 @@ public class LockSettingsServiceTestable extends LockSettingsService {
public static class MockInjector extends LockSettingsService.Injector {
private LockSettingsStorage mLockSettingsStorage;
- private KeyStore mKeyStore;
private IActivityManager mActivityManager;
private IStorageManager mStorageManager;
private SyntheticPasswordManager mSpManager;
@@ -62,14 +61,13 @@ public class LockSettingsServiceTestable extends LockSettingsService {
public boolean mIsHeadlessSystemUserMode = false;
public boolean mIsMainUserPermanentAdmin = false;
- public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
- IActivityManager activityManager,
- IStorageManager storageManager, SyntheticPasswordManager spManager,
- FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+ public MockInjector(Context context, LockSettingsStorage storage,
+ IActivityManager activityManager, IStorageManager storageManager,
+ SyntheticPasswordManager spManager, FakeGsiService gsiService,
+ RecoverableKeyStoreManager recoverableKeyStoreManager,
UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
super(context);
mLockSettingsStorage = storage;
- mKeyStore = keyStore;
mActivityManager = activityManager;
mStorageManager = storageManager;
mSpManager = spManager;
@@ -110,11 +108,6 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
- public KeyStore getKeyStore() {
- return mKeyStore;
- }
-
- @Override
public IStorageManager getStorageManager() {
return mStorageManager;
}
@@ -145,8 +138,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
- public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
- java.security.KeyStore ks) {
+ public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
return mock(UnifiedProfilePasswordCache.class);
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index d6d2b6d9abd2..07fb9fc2f509 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -52,9 +52,9 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 15cd5115a49e..124970758fa5 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -177,7 +177,6 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -191,6 +190,7 @@ import android.util.SparseIntArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -2404,6 +2404,45 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
+ public void testObsoleteHandleUidGone() throws Exception {
+ callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 51);
+ assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+
+ clearInvocations(mNetworkManager);
+
+ // In the service, handleUidGone is only called from mUidEventHandler. Then a call to it may
+ // be rendered obsolete by a newer uid change posted on the handler. The latest uid state
+ // change is always reflected in the current UidStateChangeCallbackInfo for the uid, so to
+ // simulate an obsolete call for test, we directly call handleUidGone and leave the state in
+ // UidStateChangeCallbackInfo set by the previous call to onUidStateChanged(TOP). This call
+ // should then do nothing.
+ mService.handleUidGone(UID_A);
+
+ verify(mNetworkManager, times(0)).setFirewallUidRule(anyInt(), anyInt(), anyInt());
+ assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
+ public void testObsoleteHandleUidChanged() throws Exception {
+ callAndWaitOnUidGone(UID_A);
+ assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+
+ clearInvocations(mNetworkManager);
+
+ // In the service, handleUidChanged is only called from mUidEventHandler. Then a call to it
+ // may be rendered obsolete by an immediate uid-gone posted on the handler. The latest uid
+ // state change is always reflected in the current UidStateChangeCallbackInfo for the uid,
+ // so to simulate an obsolete call for test, we directly call handleUidChanged and leave the
+ // state in UidStateChangeCallbackInfo as null as it would get removed by the previous call
+ // to onUidGone(). This call should then do nothing.
+ mService.handleUidChanged(UID_A);
+
+ verify(mNetworkManager, times(0)).setFirewallUidRule(anyInt(), anyInt(), anyInt());
+ assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+ }
+
+ @Test
public void testLowPowerStandbyAllowlist() throws Exception {
// Chain background is also enabled but these procstates are important enough to be exempt.
callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0);
diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
index ea84eb2fbf73..a6f2196cf05b 100644
--- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
@@ -16,8 +16,6 @@
package com.android.server.os;
-import android.app.admin.flags.Flags;
-
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.google.common.truth.Truth.assertThat;
@@ -27,15 +25,21 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.flags.Flags;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.BugreportManager.BugreportCallback;
+import android.os.BugreportParams;
import android.os.IBinder;
import android.os.IDumpstateListener;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -65,6 +69,9 @@ import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
public class BugreportManagerServiceImplTest {
+ private static final UserInfo ADMIN_USER_INFO =
+ new UserInfo(/* id= */ 5678, "adminUser", UserInfo.FLAG_ADMIN);
+
@Rule
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -75,6 +82,12 @@ public class BugreportManagerServiceImplTest {
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private UserManager mMockUserManager;
+ @Mock
+ private DevicePolicyManager mMockDevicePolicyManager;
+
+ private TestInjector mInjector;
private int mCallingUid = 1234;
private String mCallingPackage = "test.package";
@@ -90,11 +103,13 @@ public class BugreportManagerServiceImplTest {
mMappingFile = new AtomicFile(mContext.getFilesDir(), "bugreport-mapping.xml");
ArraySet<String> mAllowlistedPackages = new ArraySet<>();
mAllowlistedPackages.add(mContext.getPackageName());
- mService = new BugreportManagerServiceImpl(
- new BugreportManagerServiceImpl.Injector(mContext, mAllowlistedPackages,
- mMappingFile));
+ mInjector = new TestInjector(mContext, mAllowlistedPackages, mMappingFile,
+ mMockUserManager, mMockDevicePolicyManager);
+ mService = new BugreportManagerServiceImpl(mInjector);
mBugreportFileManager = new BugreportManagerServiceImpl.BugreportFileManager(mMappingFile);
when(mPackageManager.getPackageUidAsUser(anyString(), anyInt())).thenReturn(mCallingUid);
+ // The calling user is an admin user by default.
+ when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(true);
}
@After
@@ -182,6 +197,63 @@ public class BugreportManagerServiceImplTest {
}
@Test
+ public void testStartBugreport() throws Exception {
+ mService.startBugreport(mCallingUid, mContext.getPackageName(),
+ new FileDescriptor(), /* screenshotFd= */ null,
+ BugreportParams.BUGREPORT_MODE_FULL,
+ /* flags= */ 0, new Listener(new CountDownLatch(1)),
+ /* isScreenshotRequested= */ false);
+
+ assertThat(mInjector.isBugreportStarted()).isTrue();
+ }
+
+ @Test
+ public void testStartBugreport_nonAdminProfileOfAdminCurrentUser() throws Exception {
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = UserHandle.getUserId(callingUid);
+ when(mMockUserManager.isUserAdmin(callingUserId)).thenReturn(false);
+ when(mMockUserManager.getProfileParent(callingUserId)).thenReturn(ADMIN_USER_INFO);
+
+ mService.startBugreport(mCallingUid, mContext.getPackageName(),
+ new FileDescriptor(), /* screenshotFd= */ null,
+ BugreportParams.BUGREPORT_MODE_FULL,
+ /* flags= */ 0, new Listener(new CountDownLatch(1)),
+ /* isScreenshotRequested= */ false);
+
+ assertThat(mInjector.isBugreportStarted()).isTrue();
+ }
+
+ @Test
+ public void testStartBugreport_throwsForNonAdminUser() throws Exception {
+ when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false);
+
+ Exception thrown = assertThrows(IllegalArgumentException.class,
+ () -> mService.startBugreport(mCallingUid, mContext.getPackageName(),
+ new FileDescriptor(), /* screenshotFd= */ null,
+ BugreportParams.BUGREPORT_MODE_FULL,
+ /* flags= */ 0, new Listener(new CountDownLatch(1)),
+ /* isScreenshotRequested= */ false));
+
+ assertThat(thrown.getMessage()).contains("not an admin user");
+ }
+
+ @Test
+ public void testStartBugreport_throwsForNotAffiliatedUser() throws Exception {
+ when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false);
+ when(mMockDevicePolicyManager.getDeviceOwnerUserId()).thenReturn(-1);
+ when(mMockDevicePolicyManager.isAffiliatedUser(anyInt())).thenReturn(false);
+
+ Exception thrown = assertThrows(IllegalArgumentException.class,
+ () -> mService.startBugreport(mCallingUid, mContext.getPackageName(),
+ new FileDescriptor(), /* screenshotFd= */ null,
+ BugreportParams.BUGREPORT_MODE_REMOTE,
+ /* flags= */ 0, new Listener(new CountDownLatch(1)),
+ /* isScreenshotRequested= */ false));
+
+ assertThat(thrown.getMessage()).contains("not affiliated to the device owner");
+ }
+
+ @Test
public void testRetrieveBugreportWithoutFilesForCaller() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Listener listener = new Listener(latch);
@@ -224,7 +296,8 @@ public class BugreportManagerServiceImplTest {
private void clearAllowlist() {
mService = new BugreportManagerServiceImpl(
- new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>(), mMappingFile));
+ new TestInjector(mContext, new ArraySet<>(), mMappingFile,
+ mMockUserManager, mMockDevicePolicyManager));
}
private static class Listener implements IDumpstateListener {
@@ -275,4 +348,46 @@ public class BugreportManagerServiceImplTest {
complete(successful);
}
}
+
+ private static class TestInjector extends BugreportManagerServiceImpl.Injector {
+
+ private static final String SYSTEM_PROPERTY_BUGREPORT_START = "ctl.start";
+ private static final String SYSTEM_PROPERTY_BUGREPORT_STOP = "ctl.stop";
+
+ private final UserManager mUserManager;
+ private final DevicePolicyManager mDevicePolicyManager;
+ private boolean mBugreportStarted = false;
+
+ TestInjector(Context context, ArraySet<String> allowlistedPackages, AtomicFile mappingFile,
+ UserManager um, DevicePolicyManager dpm) {
+ super(context, allowlistedPackages, mappingFile);
+ mUserManager = um;
+ mDevicePolicyManager = dpm;
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ @Override
+ public DevicePolicyManager getDevicePolicyManager() {
+ return mDevicePolicyManager;
+ }
+
+ @Override
+ public void setSystemProperty(String key, String value) {
+ // Calling SystemProperties.set() will throw a RuntimeException due to permission error.
+ // Instead, we are just marking a flag to store the state for testing.
+ if (SYSTEM_PROPERTY_BUGREPORT_START.equals(key)) {
+ mBugreportStarted = true;
+ } else if (SYSTEM_PROPERTY_BUGREPORT_STOP.equals(key)) {
+ mBugreportStarted = false;
+ }
+ }
+
+ public boolean isBugreportStarted() {
+ return mBugreportStarted;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 44c464ed6adf..507b3fe62e29 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -40,13 +40,13 @@ import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.provider.Settings;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
import android.util.ArraySet;
import android.util.Slog;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
diff --git a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
index 0635cc4239f4..669eedf39342 100644
--- a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
@@ -32,13 +32,13 @@ import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.os.Handler;
import android.os.PowerManager;
import android.os.test.TestLooper;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableResources;
import android.view.Window;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import com.android.internal.R;
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index ca0270deb398..db7822d32a93 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -47,7 +47,8 @@ import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.attention.AttentionService;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.server.wm.WindowManagerInternal;
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 6fffd7533df8..fe9a0e220d41 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -29,10 +29,11 @@ import android.os.PowerSaveState;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.util.ArrayMap;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
import com.android.frameworks.servicestests.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
diff --git a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
index 517f48346b30..cb5371bfd4d1 100644
--- a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
@@ -26,8 +26,8 @@ import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
import android.content.res.Configuration;
import android.os.PersistableBundle;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index cd29c8057706..f3885289a977 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -31,11 +31,11 @@ import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.PersistableBundle;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
import android.util.LongSparseArray;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
index 076d5caf5954..44d116181be5 100644
--- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
@@ -147,7 +147,7 @@ public class AnrTimerTest {
final int n = 4;
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
if (stack.length < n+1) return "test";
- return stack[n].getMethodName();
+ return stack[n].getClassName() + "." + stack[n].getMethodName();
}
}
@@ -318,8 +318,11 @@ public class AnrTimerTest {
public void testDumpOutput() throws Exception {
if (!AnrTimer.nativeTimersSupported()) return;
+ // The timers in this class are named "class.method".
+ final String timerName = "timer: com.android.server.utils.AnrTimerTest";
+
String r1 = getDumpOutput();
- assertThat(r1).doesNotContain("timer:");
+ assertThat(r1).doesNotContain(timerName);
Helper helper = new Helper(2);
TestArg t1 = new TestArg(1, 1);
@@ -333,14 +336,14 @@ public class AnrTimerTest {
String r2 = getDumpOutput();
// There are timers in the list if and only if the feature is enabled.
if (mEnabled) {
- assertThat(r2).contains("timer:");
+ assertThat(r2).contains(timerName);
} else {
- assertThat(r2).doesNotContain("timer:");
+ assertThat(r2).doesNotContain(timerName);
}
}
String r3 = getDumpOutput();
- assertThat(r3).doesNotContain("timer:");
+ assertThat(r3).doesNotContain(timerName);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index 2039f93b9c40..54d11387752c 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -96,6 +96,9 @@ public class TestSystemImpl implements SystemInterface {
return;
}
PackageInfo packageInfo = userPackages.get(userId);
+ if (packageInfo == null) {
+ return;
+ }
packageInfo.applicationInfo.enabled = enable;
setPackageInfoForUser(userId, packageInfo);
}
@@ -106,6 +109,9 @@ public class TestSystemImpl implements SystemInterface {
return;
}
PackageInfo packageInfo = userPackages.get(userId);
+ if (packageInfo == null) {
+ return;
+ }
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
packageInfo.applicationInfo.privateFlags &= (~ApplicationInfo.PRIVATE_FLAG_HIDDEN);
setPackageInfoForUser(userId, packageInfo);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 53c172a191b6..e181a513b637 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -31,7 +31,6 @@ import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.test.suitebuilder.annotation.MediumTest;
import android.util.Base64;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
@@ -39,6 +38,7 @@ import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
@@ -1551,7 +1551,7 @@ public class WebViewUpdateServiceTest {
@Test
@RequiresFlagsEnabled("android.webkit.update_service_v2")
- public void testDefaultWebViewPackageInstalling() {
+ public void testDefaultWebViewPackageInstallingDuringStartUp() {
String testPackage = "testDefault";
WebViewProviderInfo[] packages =
new WebViewProviderInfo[] {
@@ -1574,6 +1574,68 @@ public class WebViewUpdateServiceTest {
Matchers.anyObject(), Mockito.eq(testPackage));
}
+ @Test
+ @RequiresFlagsEnabled("android.webkit.update_service_v2")
+ public void testDefaultWebViewPackageInstallingAfterStartUp() {
+ String testPackage = "testDefault";
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ testPackage,
+ "",
+ true /* default available */,
+ false /* fallback */,
+ null)
+ };
+ checkCertainPackageUsedAfterWebViewBootPreparation(testPackage, packages);
+
+ // uninstall the default package.
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(
+ testPackage, true /* enabled */, true /* valid */, false /* installed */));
+ mWebViewUpdateServiceImpl.packageStateChanged(testPackage,
+ WebViewUpdateService.PACKAGE_REMOVED, 0);
+
+ // Check that we try to re-install the default package.
+ Mockito.verify(mTestSystemImpl)
+ .installExistingPackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(testPackage));
+ }
+
+ /**
+ * Ensures that adding a new user for which the current WebView package is uninstalled triggers
+ * the repair logic.
+ */
+ @Test
+ @RequiresFlagsEnabled("android.webkit.update_service_v2")
+ public void testAddingNewUserWithDefaultdPackageNotInstalled() {
+ String testPackage = "testDefault";
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ testPackage,
+ "",
+ true /* default available */,
+ false /* fallback */,
+ null)
+ };
+ checkCertainPackageUsedAfterWebViewBootPreparation(testPackage, packages);
+
+ // Add new user with the default package not installed.
+ int newUser = 100;
+ mTestSystemImpl.addUser(newUser);
+ mTestSystemImpl.setPackageInfoForUser(newUser,
+ createPackageInfo(testPackage, true /* enabled */, true /* valid */,
+ false /* installed */));
+
+ mWebViewUpdateServiceImpl.handleNewUser(newUser);
+
+ // Check that we try to re-install the default package for all users.
+ Mockito.verify(mTestSystemImpl)
+ .installExistingPackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(testPackage));
+ }
+
private void testDefaultPackageChosen(PackageInfo packageInfo) {
WebViewProviderInfo[] packages =
new WebViewProviderInfo[] {
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 2f29d10ec2f9..515898a883e8 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -48,6 +48,8 @@ android_test {
"notification_flags_lib",
"platform-test-rules",
"SettingsLib",
+ "libprotobuf-java-lite",
+ "platformprotoslite",
],
libs: [
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java b/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
index dc7f118628b7..f229fd9bb221 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
@@ -20,8 +20,7 @@ import static com.android.server.notification.AlertRateLimiter.ALLOWED_ALERT_INT
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import android.test.suitebuilder.annotation.SmallTest;
-
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
index 8bc027d50a3b..39ff9cc75fc2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
@@ -33,8 +33,8 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
index ce6939a9beeb..2f5c96c36a41 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
@@ -35,8 +35,8 @@ import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
index 9dffed20afea..b5bc610f82ea 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
@@ -48,8 +48,8 @@ import android.content.pm.ShortcutInfo;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 8622488f820e..517dcb4f21f6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -74,12 +74,12 @@ import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.InstanceIdSequence;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 3797dbb97213..bfbc81ccfe39 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -29,9 +29,11 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -103,8 +105,10 @@ public class DefaultDeviceEffectsApplierTest {
mContext.addMockSystemService(ColorDisplayManager.class, mColorDisplayManager);
mContext.addMockSystemService(UiModeManager.class, mUiModeManager);
mContext.addMockSystemService(WallpaperManager.class, mWallpaperManager);
+ when(mWallpaperManager.isWallpaperSupported()).thenReturn(true);
mApplier = new DefaultDeviceEffectsApplier(mContext);
+ verify(mWallpaperManager).isWallpaperSupported();
}
@Test
@@ -187,6 +191,26 @@ public class DefaultDeviceEffectsApplierTest {
}
@Test
+ public void apply_disabledWallpaperService_dimWallpaperNotApplied() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ WallpaperManager disabledWallpaperService = mock(WallpaperManager.class);
+ when(mWallpaperManager.isWallpaperSupported()).thenReturn(false);
+ mContext.addMockSystemService(WallpaperManager.class, disabledWallpaperService);
+ mApplier = new DefaultDeviceEffectsApplier(mContext);
+ verify(mWallpaperManager).isWallpaperSupported();
+
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(true)
+ .build();
+ mApplier.apply(effects, UPDATE_ORIGIN_USER);
+
+ verifyNoMoreInteractions(mWallpaperManager);
+ }
+
+ @Test
public void apply_someEffects_onlyThoseEffectsApplied() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
index 5041779840b3..d3e1b902cea0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -22,8 +22,8 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index f0779144ad18..1194973b4cad 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -57,9 +57,9 @@ import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
index ffc0dcdca5fc..a11d3f4a8d2c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -16,9 +16,6 @@
package com.android.server.notification;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
@@ -27,8 +24,8 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 4dded1d0342d..05b6c907069b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -37,6 +37,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -885,6 +886,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, true);
service.reregisterService(cn, 0);
@@ -915,6 +917,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, false);
service.reregisterService(cn, 0);
@@ -945,6 +948,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, true);
service.reregisterService(cn, 0);
@@ -975,6 +979,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, false);
service.reregisterService(cn, 0);
@@ -1152,6 +1157,58 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ public void testUpgradeAppNoPermissionNoRebind() throws Exception {
+ Context context = spy(getContext());
+ doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
+ mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ List<String> packages = new ArrayList<>();
+ packages.add("package");
+ addExpectedServices(service, packages, 0);
+
+ final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
+ final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
+
+ // Both components are approved initially
+ mExpectedPrimaryComponentNames.clear();
+ mExpectedPrimaryPackages.clear();
+ mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+ mExpectedSecondaryComponentNames.clear();
+ mExpectedSecondaryPackages.clear();
+
+ loadXml(service);
+
+ //Component package/C1 loses bind permission
+ when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
+ (Answer<ServiceInfo>) invocation -> {
+ ComponentName invocationCn = invocation.getArgument(0);
+ if (invocationCn != null) {
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = invocationCn.getPackageName();
+ serviceInfo.name = invocationCn.getClassName();
+ if (invocationCn.equals(unapprovedComponent)) {
+ serviceInfo.permission = "none";
+ } else {
+ serviceInfo.permission = service.getConfig().bindPermission;
+ }
+ serviceInfo.metaData = null;
+ return serviceInfo;
+ }
+ return null;
+ }
+ );
+
+ // Trigger package update
+ service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+ assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
+ assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
+ }
+
+ @Test
public void testSetPackageOrComponentEnabled() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 33ca5c2bbe16..a45b102278ef 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -20,6 +20,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
@@ -32,6 +33,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.INotificationManager;
import android.content.ComponentName;
import android.content.Context;
@@ -47,9 +49,12 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Xml;
+import android.Manifest;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.function.TriPredicate;
import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
@@ -59,7 +64,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -89,11 +96,15 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
+ ComponentName mCn = new ComponentName("a", "b");
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext.setMockPackageManager(mPm);
mContext.addMockSystemService(Context.USER_SERVICE, mUm);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.string.config_defaultAssistantAccessComponent, "a/a");
mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
when(mNm.getBinderService()).thenReturn(mINm);
mContext.ensureTestableResources();
@@ -102,8 +113,9 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
ResolveInfo resolve = new ResolveInfo();
approved.add(resolve);
ServiceInfo info = new ServiceInfo();
- info.packageName = "a";
- info.name="a";
+ info.packageName = mCn.getPackageName();
+ info.name = mCn.getClassName();
+ info.permission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
resolve.serviceInfo = info;
when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
.thenReturn(approved);
@@ -137,6 +149,51 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
}
@Test
+ public void testWriteXml_userTurnedOffNAS() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+
+ mAssistants.loadDefaultsFromConfig(true);
+
+ mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
+ true, true);
+
+ ComponentName current = CollectionUtils.firstOrNull(
+ mAssistants.getAllowedComponents(userId));
+ assertNotNull(current);
+ mAssistants.setUserSet(userId, true);
+ mAssistants.setPackageOrComponentEnabled(current.flattenToString(), userId, true, false,
+ true);
+
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mAssistants.writeXml(serializer, true, userId);
+ serializer.endDocument();
+ serializer.flush();
+
+ //fail(baos.toString("UTF-8"));
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0);
+ // approved should not be null
+ assertNotNull(approved);
+ assertEquals(new ArraySet<>(), approved.get(true));
+
+ // user set is maintained
+ assertTrue(mAssistants.mIsUserChanged.get(ActivityManager.getCurrentUser()));
+ }
+
+ @Test
public void testReadXml_userDisabled() throws Exception {
String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+ "<service_listing approved=\"\" user=\"0\" primary=\"true\""
@@ -160,6 +217,33 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
}
@Test
+ public void testReadXml_userDisabled_restore() throws Exception {
+ String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+ + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+ + "user_changed=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, true,
+ ActivityManager.getCurrentUser());
+
+ ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0);
+
+ // approved should not be null
+ assertNotNull(approved);
+ assertEquals(new ArraySet<>(), approved.get(true));
+
+ // user set is maintained
+ assertTrue(mAssistants.mIsUserChanged.get(ActivityManager.getCurrentUser()));
+ }
+
+ @Test
public void testReadXml_upgradeUserSet() throws Exception {
String xml = "<enabled_assistants version=\"3\" defaults=\"b/b\">"
+ "<service_listing approved=\"\" user=\"0\" primary=\"true\""
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index e75afccfdfdf..a1c24f1f27bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -34,10 +34,9 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
@@ -84,13 +83,13 @@ import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
@@ -103,10 +102,6 @@ import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.pm.PackageManagerService;
-import java.util.List;
-import java.util.Objects;
-
-import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -118,6 +113,10 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@SuppressLint("GuardedBy")
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 7b16500928a0..5aecac2cab78 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -50,8 +50,8 @@ import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java
index 10bfcf12c89f..6faa89970bf7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryFilterTest.java
@@ -20,9 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.NotificationHistory;
import android.app.NotificationHistory.HistoricalNotification;
import android.graphics.drawable.Icon;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
index 3499a12f5954..bf8cfa5c0561 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
@@ -20,8 +20,12 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -70,10 +74,11 @@ public class NotificationHistoryJobServiceTest extends UiServiceTestCase {
@Before
public void setUp() throws Exception {
- mJobService = new NotificationHistoryJobService();
+ mJobService = spy(new NotificationHistoryJobService());
mJobService.attachBaseContext(mContext);
mJobService.onCreate();
mJobService.onBind(/* intent= */ null); // Create JobServiceEngine within JobService.
+ doNothing().when(mJobService).jobFinished(any(), eq(false));
mContext.addMockSystemService(JobScheduler.class, mMockJobScheduler);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
index 6eaf5462a6ec..b234c3ea44c0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
@@ -20,10 +20,10 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.NotificationHistory;
import android.app.NotificationHistory.HistoricalNotification;
import android.graphics.drawable.Icon;
-import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index bf850cfe04db..8fad01a7541d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -56,8 +56,8 @@ import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 715c9d4081b2..e3ea55a67e71 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -87,10 +87,10 @@ import static android.service.notification.Adjustment.KEY_CONTEXTUAL_ACTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
-import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
@@ -247,7 +247,6 @@ import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -263,6 +262,7 @@ import android.util.Xml;
import android.widget.RemoteViews;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -14334,6 +14334,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void requestInterruptionFilterFromListener_fromApp_doesNotSetGlobalZen()
throws Exception {
mService.setCallerIsNormalPackage();
@@ -14351,6 +14352,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void requestInterruptionFilterFromListener_fromSystem_setsGlobalZen()
throws Exception {
mService.isSystemUid = true;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
index 0222bfbf8605..b42df77270ee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
@@ -22,7 +22,6 @@ import static junit.framework.Assert.assertSame;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,12 +42,12 @@ import android.os.Handler;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.filters.SmallTest;
+
import com.android.server.UiServiceTestCase;
import org.junit.Before;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
index 0c6283116594..30e851f0423c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
@@ -20,8 +20,8 @@ import static junit.framework.Assert.assertTrue;
import android.os.Parcel;
import android.service.notification.NotifyingApp;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 2f52d5c48b6b..2d591ba0b98b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -43,9 +43,9 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.ParceledListSlice;
import android.permission.IPermissionManager;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8b557789195a..bfc47fdef5cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -115,7 +115,6 @@ import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.nano.RankingHelperProto;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -128,6 +127,7 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
@@ -324,7 +324,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
.thenReturn(appPermissions);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
+ IntArray currentProfileIds = IntArray.wrap(new int[]{0});
+ if (UserManager.isHeadlessSystemUserMode()) {
+ currentProfileIds.add(UserHandle.getUserId(UID_HEADLESS));
+ }
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(currentProfileIds);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 4217881495b1..d2c6028dea45 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -43,10 +43,10 @@ import android.os.Build;
import android.os.UserHandle;
import android.os.Vibrator;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
index 131aaa0d4ea7..65ed7b6e622d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
@@ -19,8 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.HOURS;
-import android.test.suitebuilder.annotation.SmallTest;
-
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 81c573d8fb1e..70910b108fe9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -54,7 +54,6 @@ import android.os.UserManager;
import android.permission.PermissionManager;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -63,6 +62,7 @@ import android.util.AtomicFile;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.logging.InstanceIdSequence;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 9ad007d6a840..7b4229f62709 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -23,9 +23,9 @@ import static org.junit.Assert.assertTrue;
import android.service.notification.ScheduleCalendar;
import android.service.notification.ZenModeConfig;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index 11cb150651aa..a4fb16dc1adc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -36,9 +36,9 @@ import android.content.pm.ShortcutServiceInternal;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableLooper;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 1e3b7282e2f7..dcd56e07f0d2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -44,10 +44,10 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.IntArray;
import android.util.Xml;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.TypedXmlPullParser;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index 0564a73bf3fc..cb4422235ea4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -45,11 +45,11 @@ import android.os.Bundle;
import android.os.UserManager;
import android.provider.ContactsContract;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableString;
import android.util.ArraySet;
import android.util.LruCache;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
index f135d16ea3b8..0993bec42c6a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
@@ -24,8 +24,8 @@ import static org.mockito.Mockito.when;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java
index 3998129659c6..6084153cf430 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java
@@ -18,17 +18,11 @@ package com.android.server.notification;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.Notification.VISIBILITY_SECRET;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.when;
@@ -36,15 +30,11 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
-import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.media.session.MediaSession;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
index 99d5a6d9118a..75552bc433c5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
@@ -16,7 +16,7 @@
package com.android.server.notification;
-import static com.android.server.notification.ZenAdapters.notificationPolicyToZenPolicy;
+import static android.service.notification.ZenAdapters.notificationPolicyToZenPolicy;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java
new file mode 100644
index 000000000000..f724510eeb73
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AutomaticZenRule;
+import android.provider.Settings;
+import android.service.notification.ZenPolicy;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.os.dnd.ActiveRuleType;
+import com.android.os.dnd.ChannelPolicy;
+import com.android.os.dnd.ConversationType;
+import com.android.os.dnd.PeopleType;
+import com.android.os.dnd.State;
+import com.android.os.dnd.ZenMode;
+
+import com.google.protobuf.Internal;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/** Test to validate that logging enums used in Zen classes match their API definitions. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ZenEnumTest {
+
+ @Test
+ public void testEnum_zenMode() {
+ testEnum(Settings.Global.class, "ZEN_MODE", ZenMode.class, "ZEN_MODE");
+ }
+
+ @Test
+ public void testEnum_activeRuleType() {
+ testEnum(AutomaticZenRule.class, "TYPE", ActiveRuleType.class, "TYPE");
+ }
+
+ @Test
+ public void testEnum_zenPolicyState() {
+ testEnum(ZenPolicy.class, "STATE", State.class, "STATE");
+ }
+
+ @Test
+ public void testEnum_zenPolicyChannelPolicy() {
+ testEnum(ZenPolicy.class, "CHANNEL_POLICY", ChannelPolicy.class, "CHANNEL_POLICY");
+ }
+
+ @Test
+ public void testEnum_zenPolicyConversationType() {
+ testEnum(ZenPolicy.class, "CONVERSATION_SENDERS", ConversationType.class, "CONV");
+ }
+
+ @Test
+ public void testEnum_zenPolicyPeopleType() {
+ testEnum(ZenPolicy.class, "PEOPLE_TYPE", PeopleType.class, "PEOPLE");
+ }
+
+ /**
+ * Verifies that any constants (i.e. {@code public static final int} fields) named {@code
+ * <apiPrefix>_SOMETHING} in {@code apiClass} are present and have the same numerical value
+ * in the enum values defined in {@code loggingProtoEnumClass}.
+ *
+ * <p>Note that <em>extra</em> values in the logging enum are accepted (since we have one of
+ * those, and the main goal of this test is that we don't forget to update the logging enum
+ * if new API enum values are added).
+ */
+ private static void testEnum(Class<?> apiClass, String apiPrefix,
+ Class<? extends Internal.EnumLite> loggingProtoEnumClass,
+ String loggingPrefix) {
+ Map<String, Integer> apiConstants =
+ Arrays.stream(apiClass.getDeclaredFields())
+ .filter(f -> Modifier.isPublic(f.getModifiers()))
+ .filter(f -> Modifier.isStatic(f.getModifiers()))
+ .filter(f -> Modifier.isFinal(f.getModifiers()))
+ .filter(f -> f.getType().equals(int.class))
+ .filter(f -> f.getName().startsWith(apiPrefix + "_"))
+ .collect(Collectors.toMap(
+ Field::getName,
+ ZenEnumTest::getStaticFieldIntValue));
+
+ Map<String, Integer> loggingConstants =
+ Arrays.stream(loggingProtoEnumClass.getEnumConstants())
+ .collect(Collectors.toMap(
+ v -> v.toString(),
+ v -> v.getNumber()));
+
+ Map<String, Integer> renamedApiConstants = apiConstants.entrySet().stream()
+ .collect(Collectors.toMap(
+ entry -> entry.getKey().replace(apiPrefix + "_", loggingPrefix + "_"),
+ Map.Entry::getValue));
+
+ assertThat(loggingConstants).containsAtLeastEntriesIn(renamedApiConstants);
+ }
+
+ private static int getStaticFieldIntValue(Field f) {
+ try {
+ return f.getInt(null);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index 7d6e12cf7cb4..b997f5d9a2a0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -51,11 +51,12 @@ import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
+import androidx.test.filters.SmallTest;
+
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 8c2fd1013056..7c1adbc39033 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -22,6 +22,7 @@ import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
@@ -108,6 +109,7 @@ import android.app.AutomaticZenRule;
import android.app.Flags;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
+import android.app.compat.CompatChanges;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.pm.ActivityInfo;
@@ -134,6 +136,7 @@ import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.DeviceEffectsApplier;
+import android.service.notification.ZenAdapters;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
@@ -141,7 +144,6 @@ import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeDiff;
import android.service.notification.ZenPolicy;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestWithLooperRule;
import android.testing.TestableLooper;
import android.util.ArrayMap;
@@ -150,6 +152,8 @@ import android.util.StatsEvent;
import android.util.StatsEventTestUtils;
import android.util.Xml;
+import androidx.test.filters.SmallTest;
+
import com.android.internal.R;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -4673,7 +4677,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
- assertEquals(AUTOMATIC_RULE_STATUS_ACTIVATED, actualStatus[0]);
+ if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
+ assertEquals(AUTOMATIC_RULE_STATUS_ACTIVATED, actualStatus[0]);
+ } else {
+ assertEquals(AUTOMATIC_RULE_STATUS_UNKNOWN, actualStatus[0]);
+ }
}
@Test
@@ -4714,7 +4722,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
null, "", Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
- assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
+ assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ } else {
+ assertEquals(AUTOMATIC_RULE_STATUS_UNKNOWN, actualStatus[1]);
+ }
}
@Test
@@ -4753,10 +4765,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_FALSE),
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ UPDATE_ORIGIN_APP, Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
- assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
+ assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ } else {
+ assertEquals(AUTOMATIC_RULE_STATUS_UNKNOWN, actualStatus[1]);
+ }
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
index 3a88294a0fa1..6433b76defc3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
@@ -26,8 +26,8 @@ import android.os.Parcel;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
import android.service.notification.nano.DNDPolicyProto;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java
new file mode 100644
index 000000000000..038f1db32d18
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.util.IndentingPrintWriter;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.vibrator.GroupedAggregatedLogRecords.AggregatedLogRecord;
+import com.android.server.vibrator.GroupedAggregatedLogRecords.SingleLogRecord;
+
+import org.junit.Test;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class GroupedAggregatedLogRecordsTest {
+
+ private static final int AGGREGATION_TIME_LIMIT = 1000;
+ private static final int NO_AGGREGATION_TIME_LIMIT = 0;
+ private static final long PROTO_FIELD_ID = 1;
+ private static final int GROUP_1 = 1;
+ private static final int GROUP_2 = 2;
+ private static final int KEY_1 = 1;
+ private static final int KEY_2 = 2;
+
+ private static final IndentingPrintWriter WRITER = new IndentingPrintWriter(new StringWriter());
+ private static final ProtoOutputStream PROTO_OUTPUT_STREAM = new ProtoOutputStream();
+
+ private final List<TestSingleLogRecord> mTestRecords = new ArrayList<>();
+
+ @Test
+ public void record_noAggregation_keepsIndividualRecords() {
+ int sizeLimit = 10;
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ sizeLimit, NO_AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ for (int i = 0; i < sizeLimit; i++) {
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+ }
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeWrittenOnce(0, sizeLimit);
+ }
+
+ @Test
+ public void record_sizeLimit_dropsOldestEntriesForNewOnes() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 2, NO_AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ TestSingleLogRecord firstRecord = createRecord(GROUP_1, KEY_1, createTime++);
+ assertThat(records.add(firstRecord)).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+
+ // Adding third record drops first record
+ AggregatedLogRecord<TestSingleLogRecord> droppedRecord =
+ records.add(createRecord(GROUP_1, KEY_1, createTime++));
+ assertThat(droppedRecord).isNotNull();
+ assertThat(droppedRecord.getLatest()).isEqualTo(firstRecord);
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeNotWritten(0, 1); // First record not written
+ assertRecordsInRangeWrittenOnce(1, 3); // All newest records written
+ }
+
+ @Test
+ public void record_timeAggregation_aggregatesCloseRecordAndPrintsOnlyFirstAndLast() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ // No record dropped, all aggregated in a single entry
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime + 1))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1,
+ createTime + AGGREGATION_TIME_LIMIT - 2))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1,
+ createTime + AGGREGATION_TIME_LIMIT - 1))).isNull();
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeWrittenOnce(0, 1); // Writes first record
+ assertRecordsInRangeNotWritten(1, 3); // Skips aggregated records in between
+ assertRecordsInRangeWrittenOnce(3, 4); // Writes last record
+ }
+
+ @Test
+ public void record_differentGroups_recordsKeptSeparate() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ // No record dropped, all kept in separate aggregated lists
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+ assertThat(records.add(createRecord(GROUP_2, KEY_2, createTime++))).isNull();
+ assertThat(records.add(createRecord(GROUP_2, KEY_2, createTime++))).isNull();
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1, GROUP_2);
+ assertRecordsInRangeWrittenOnce(0, 4);
+ }
+
+ @Test
+ public void record_sameGroupDifferentAggregationKeys_recordsNotAggregated() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+
+ // Second record on same group with different key not aggregated, drops first record
+ AggregatedLogRecord<TestSingleLogRecord> droppedRecord =
+ records.add(createRecord(GROUP_1, KEY_2, createTime++));
+ assertThat(droppedRecord).isNotNull();
+ assertThat(droppedRecord.getLatest()).isEqualTo(mTestRecords.getFirst());
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeNotWritten(0, 1); // Skips first record that was dropped
+ assertRecordsInRangeWrittenOnce(1, 2); // Writes last record
+ }
+
+ @Test
+ public void record_sameGroupAndAggregationKeysDistantTimes_recordsNotAggregated() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime))).isNull();
+
+ // Second record after aggregation time limit not aggregated, drops first record
+ AggregatedLogRecord<TestSingleLogRecord> droppedRecord =
+ records.add(createRecord(GROUP_1, KEY_1, createTime + AGGREGATION_TIME_LIMIT));
+ assertThat(droppedRecord).isNotNull();
+ assertThat(droppedRecord.getLatest()).isEqualTo(mTestRecords.getFirst());
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeNotWritten(0, 1); // Skips first record that was dropped
+ assertRecordsInRangeWrittenOnce(1, 2); // Writes last record
+ }
+
+ private TestSingleLogRecord createRecord(int groupKey, int aggregateKey, long createTime) {
+ TestSingleLogRecord record = new TestSingleLogRecord(groupKey, aggregateKey, createTime);
+ mTestRecords.add(record);
+ return record;
+ }
+
+ private void dumpRecords(TestGroupedAggregatedLogRecords records) {
+ records.dump(WRITER);
+ records.dump(PROTO_OUTPUT_STREAM);
+ }
+
+ private void assertGroupHeadersWrittenOnce(TestGroupedAggregatedLogRecords records,
+ int... groupKeys) {
+ assertThat(records.dumpGroupKeys).containsExactlyElementsIn(
+ Arrays.stream(groupKeys).boxed().toList());
+ }
+
+ private void assertRecordsInRangeWrittenOnce(int startIndexInclusive, int endIndexExclusive) {
+ for (int i = startIndexInclusive; i < endIndexExclusive; i++) {
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpTextCount)
+ .isEqualTo(1);
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpProtoFieldIds)
+ .containsExactly(PROTO_FIELD_ID);
+ }
+ }
+
+ private void assertRecordsInRangeNotWritten(int startIndexInclusive, int endIndexExclusive) {
+ for (int i = startIndexInclusive; i < endIndexExclusive; i++) {
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpTextCount)
+ .isEqualTo(0);
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpProtoFieldIds)
+ .isEmpty();
+ }
+ }
+
+ private static final class TestGroupedAggregatedLogRecords
+ extends GroupedAggregatedLogRecords<TestSingleLogRecord> {
+
+ public final List<Integer> dumpGroupKeys = new ArrayList<>();
+
+ private final long mProtoFieldId;
+
+ TestGroupedAggregatedLogRecords(int sizeLimit, int aggregationTimeLimitMs,
+ long protoFieldId) {
+ super(sizeLimit, aggregationTimeLimitMs);
+ mProtoFieldId = protoFieldId;
+ }
+
+ @Override
+ void dumpGroupHeader(IndentingPrintWriter pw, int groupKey) {
+ dumpGroupKeys.add(groupKey);
+ }
+
+ @Override
+ long findGroupKeyProtoFieldId(int groupKey) {
+ return mProtoFieldId;
+ }
+ }
+
+ private static final class TestSingleLogRecord implements SingleLogRecord {
+ public final List<Long> dumpProtoFieldIds = new ArrayList<>();
+ public int dumpTextCount = 0;
+
+ private final int mGroupKey;
+ private final int mAggregateKey;
+ private final long mCreateTime;
+
+ TestSingleLogRecord(int groupKey, int aggregateKey, long createTime) {
+ mGroupKey = groupKey;
+ mAggregateKey = aggregateKey;
+ mCreateTime = createTime;
+ }
+
+ @Override
+ public int getGroupKey() {
+ return mGroupKey;
+ }
+
+ @Override
+ public long getCreateUptimeMs() {
+ return mCreateTime;
+ }
+
+ @Override
+ public boolean mayAggregate(SingleLogRecord record) {
+ if (record instanceof TestSingleLogRecord param) {
+ return mAggregateKey == param.mAggregateKey;
+ }
+ return false;
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw) {
+ dumpTextCount++;
+ }
+
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ dumpProtoFieldIds.add(fieldId);
+ }
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index 3d0dca0de87e..e3d45967848a 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -17,9 +17,11 @@
package com.android.server.vibrator;
import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
+import static android.os.VibrationAttributes.CATEGORY_UNKNOWN;
import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
import static android.os.VibrationEffect.EFFECT_CLICK;
@@ -285,7 +287,8 @@ public class HapticFeedbackVibrationProviderTest {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false);
+ SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false,
+ false /* fromIme*/);
assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isFalse();
}
@@ -295,7 +298,7 @@ public class HapticFeedbackVibrationProviderTest {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true);
+ SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true, false /* fromIme*/);
assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isTrue();
}
@@ -307,7 +310,7 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
assertWithMessage("Expected FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isTrue();
}
@@ -320,40 +323,59 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
assertWithMessage("Expected no FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isFalse();
}
}
@Test
- public void testVibrationAttribute_keyboardCategoryOff_notUseKeyboardCategory() {
+ public void testVibrationAttribute_keyboardCategoryOff_isIme_notUseKeyboardCategory() {
mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
+ assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
+ .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
assertWithMessage("Expected no CATEGORY_KEYBOARD for effect " + effectId)
- .that(attrs.getCategory()).isEqualTo(0);
+ .that(attrs.getCategory()).isEqualTo(CATEGORY_UNKNOWN);
}
}
@Test
- public void testVibrationAttribute_keyboardCategoryOn_useKeyboardCategory() {
+ public void testVibrationAttribute_keyboardCategoryOn_notIme_notUseKeyboardCategory() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
+ assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
+ .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
+ assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
+ .that(attrs.getCategory()).isEqualTo(CATEGORY_UNKNOWN);
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_keyboardCategoryOn_isIme_useKeyboardCategory() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
+ assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
+ .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
.that(attrs.getCategory()).isEqualTo(CATEGORY_KEYBOARD);
}
}
@Test
- public void testVibrationAttribute_noFixAmplitude_keyboardCategoryOn_noBypassIntensityScale() {
+ public void testVibrationAttribute_noFixAmplitude_notBypassIntensityScale() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
mockKeyboardVibrationFixedAmplitude(-1);
@@ -361,7 +383,23 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
+ assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_notIme_notBypassIntensityScale() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
@@ -369,7 +407,7 @@ public class HapticFeedbackVibrationProviderTest {
}
@Test
- public void testVibrationAttribute_fixAmplitude_keyboardCategoryOn_bypassIntensityScale() {
+ public void testVibrationAttribute_fixAmplitude_isIme_bypassIntensityScale() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
@@ -377,7 +415,7 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
assertWithMessage("Expected FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isTrue();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index 3e59878f9e1e..b2644350dfdd 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -117,32 +117,32 @@ public class VibrationScalerTest {
}
@Test
- public void testGetExternalVibrationScale() {
+ public void testGetScaleLevel() {
setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_HIGH,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_LOW,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
// Vibration setting being bypassed will use default setting and not scale.
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
}
@Test
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 0d5bf95d959d..3799abc100c9 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -38,10 +38,10 @@ import android.frameworks.vibrator.ScaleParam;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.test.TestLooper;
import android.util.SparseArray;
+import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.util.ArrayUtils;
@@ -86,20 +86,20 @@ public class VibratorControlServiceTest {
ApplicationProvider.getApplicationContext(), new Handler(testLooper.getLooper()));
mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper());
- mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(),
+ mVibratorControlService = new VibratorControlService(
+ InstrumentationRegistry.getContext(), new VibratorControllerHolder(),
mMockVibrationScaler, mVibrationSettings, mLock);
}
@Test
- public void testRegisterVibratorController() throws RemoteException {
+ public void testRegisterVibratorController() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
}
@Test
- public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest()
- throws RemoteException {
+ public void testUnregisterVibratorController_providingRegisteredController_performsRequest() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
mVibratorControlService.unregisterVibratorController(mFakeVibratorController);
@@ -108,8 +108,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest()
- throws RemoteException {
+ public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() {
FakeVibratorController controller1 = new FakeVibratorController(mTestLooper.getLooper());
FakeVibratorController controller2 = new FakeVibratorController(mTestLooper.getLooper());
mVibratorControlService.registerVibratorController(controller1);
@@ -120,8 +119,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testOnRequestVibrationParamsComplete_cachesAdaptiveHapticsScalesCorrectly()
- throws RemoteException {
+ public void testOnRequestVibrationParamsComplete_cachesAdaptiveHapticsScalesCorrectly() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
int timeoutInMillis = 10;
CompletableFuture<Void> future =
@@ -148,8 +146,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testOnRequestVibrationParamsComplete_withIncorrectToken_ignoresRequest()
- throws RemoteException, InterruptedException {
+ public void testOnRequestVibrationParamsComplete_withIncorrectToken_ignoresRequest() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
int timeoutInMillis = 10;
CompletableFuture<Void> unusedFuture =
@@ -167,8 +164,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly()
- throws RemoteException {
+ public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
SparseArray<Float> vibrationScales = new SparseArray<>();
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
@@ -187,8 +183,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testSetVibrationParams_withUnregisteredController_ignoresRequest()
- throws RemoteException {
+ public void testSetVibrationParams_withUnregisteredController_ignoresRequest() {
SparseArray<Float> vibrationScales = new SparseArray<>();
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
@@ -201,8 +196,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales()
- throws RemoteException {
+ public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
int types = buildVibrationTypesMask(ScaleParam.TYPE_ALARM, ScaleParam.TYPE_NOTIFICATION);
@@ -216,8 +210,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testClearVibrationParams_withUnregisteredController_ignoresRequest()
- throws RemoteException {
+ public void testClearVibrationParams_withUnregisteredController_ignoresRequest() {
mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM,
mFakeVibratorController);
@@ -225,8 +218,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testRequestVibrationParams_createsFutureRequestProperly()
- throws RemoteException {
+ public void testRequestVibrationParams_createsFutureRequestProperly() {
int timeoutInMillis = 10;
mVibratorControlService.registerVibratorController(mFakeVibratorController);
CompletableFuture<Void> future =
@@ -243,8 +235,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testShouldRequestVibrationParams_returnsTrueForVibrationsThatShouldRequestParams()
- throws RemoteException {
+ public void testShouldRequestVibrationParams_returnsTrueForVibrationsThatShouldRequestParams() {
int[] vibrations =
new int[]{USAGE_ALARM, USAGE_RINGTONE, USAGE_MEDIA, USAGE_TOUCH, USAGE_NOTIFICATION,
USAGE_HARDWARE_FEEDBACK, USAGE_UNKNOWN, USAGE_COMMUNICATION_REQUEST};
@@ -258,8 +249,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testShouldRequestVibrationParams_unregisteredVibratorController_returnsFalse()
- throws RemoteException {
+ public void testShouldRequestVibrationParams_unregisteredVibratorController_returnsFalse() {
int[] vibrations =
new int[]{USAGE_ALARM, USAGE_RINGTONE, USAGE_MEDIA, USAGE_TOUCH, USAGE_NOTIFICATION,
USAGE_HARDWARE_FEEDBACK, USAGE_UNKNOWN, USAGE_COMMUNICATION_REQUEST};
@@ -269,7 +259,7 @@ public class VibratorControlServiceTest {
}
}
- private int buildVibrationTypesMask(int... types) {
+ private static int buildVibrationTypesMask(int... types) {
int typesMask = 0;
for (int type : types) {
typesMask |= type;
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index d2ad61f2ba9f..1ea90f55b727 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -2526,7 +2526,7 @@ public class VibratorManagerServiceTest {
int constant, boolean always) throws InterruptedException {
HalVibration vib =
service.performHapticFeedbackInternal(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
- constant, always, "some reason", service);
+ constant, always, "some reason", service, false /* fromIme */);
if (vib != null) {
vib.waitForEnd();
}
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
index 2a010f0a82a9..0cd88ef7a4b4 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
@@ -60,11 +60,7 @@ public final class FakeVibratorController extends IVibratorController.Stub {
requestTimeoutInMillis = timeoutInMillis;
mHandler.post(() -> {
if (mVibratorControlService != null) {
- try {
- mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult);
}
});
}
diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp
index 8a79fe443179..8c70851f87df 100644
--- a/services/tests/voiceinteractiontests/Android.bp
+++ b/services/tests/voiceinteractiontests/Android.bp
@@ -45,6 +45,7 @@ android_test {
"servicestests-utils-mockito-extended",
"truth",
"frameworks-base-testutils",
+ "androidx.test.rules",
],
libs: [
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
index 35170b3516b7..a9c517ddc946 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
@@ -16,19 +16,20 @@
package com.android.server.soundtrigger;
+import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
+import android.os.Binder;
import android.os.Parcel;
import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.os.Binder;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
import java.util.Arrays;
import java.util.Locale;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index ef197918deff..d6c51736316a 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -49,6 +49,8 @@
<uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/>
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
<uses-permission android:name="android.permission.MONITOR_INPUT"/>
+ <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"/>
+ <uses-permission android:name="android.permission.MANAGE_DEFAULT_APPLICATIONS"/>
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index e21388e57a9e..3c02eee7772e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -38,6 +38,7 @@ import static android.view.KeyEvent.KEYCODE_SCREENSHOT;
import static android.view.KeyEvent.KEYCODE_U;
import static android.view.KeyEvent.KEYCODE_Z;
+import android.app.role.RoleManager;
import android.content.Intent;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -51,16 +52,19 @@ import org.junit.Test;
@Presubmit
@SmallTest
public class ModifierShortcutTests extends ShortcutKeyTestBase {
- private static final SparseArray<String> META_SHORTCUTS = new SparseArray<>();
+ private static final SparseArray<String> INTENT_SHORTCUTS = new SparseArray<>();
+ private static final SparseArray<String> ROLE_SHORTCUTS = new SparseArray<>();
static {
- META_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR);
- META_SHORTCUTS.append(KEYCODE_B, Intent.CATEGORY_APP_BROWSER);
- META_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS);
- META_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL);
- META_SHORTCUTS.append(KEYCODE_K, Intent.CATEGORY_APP_CALENDAR);
- META_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS);
- META_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC);
- META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING);
+ // These shortcuts should align with those defined in bookmarks.xml
+ INTENT_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR);
+ INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS);
+ INTENT_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL);
+ INTENT_SHORTCUTS.append(KEYCODE_K, Intent.CATEGORY_APP_CALENDAR);
+ INTENT_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS);
+ INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC);
+
+ ROLE_SHORTCUTS.append(KEYCODE_B, RoleManager.ROLE_BROWSER);
+ ROLE_SHORTCUTS.append(KEYCODE_S, RoleManager.ROLE_SMS);
}
private static final int ANY_DISPLAY_ID = 123;
@@ -74,13 +78,21 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
*/
@Test
public void testMetaShortcuts() {
- for (int i = 0; i < META_SHORTCUTS.size(); i++) {
- final int keyCode = META_SHORTCUTS.keyAt(i);
- final String category = META_SHORTCUTS.valueAt(i);
+ for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) {
+ final int keyCode = INTENT_SHORTCUTS.keyAt(i);
+ final String category = INTENT_SHORTCUTS.valueAt(i);
sendKeyCombination(new int[]{KEYCODE_META_LEFT, keyCode}, 0);
mPhoneWindowManager.assertLaunchCategory(category);
}
+
+ for (int i = 0; i < ROLE_SHORTCUTS.size(); i++) {
+ final int keyCode = ROLE_SHORTCUTS.keyAt(i);
+ final String role = ROLE_SHORTCUTS.valueAt(i);
+
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, keyCode}, 0);
+ mPhoneWindowManager.assertLaunchRole(role);
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 1a26c451c076..0776c512b61d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -60,6 +60,7 @@ import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.SearchManager;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -124,6 +125,8 @@ import java.util.function.Supplier;
class TestPhoneWindowManager {
private static final long TEST_SINGLE_KEY_DELAY_MILLIS
= SingleKeyGestureDetector.MULTI_PRESS_TIMEOUT + 1000L * HW_TIMEOUT_MULTIPLIER;
+ private static final String TEST_BROWSER_ROLE_PACKAGE_NAME = "com.browser";
+ private static final String TEST_SMS_ROLE_PACKAGE_NAME = "com.sms";
private PhoneWindowManager mPhoneWindowManager;
private Context mContext;
@@ -151,6 +154,7 @@ class TestPhoneWindowManager {
@Mock private UserManagerInternal mUserManagerInternal;
@Mock private AudioManagerInternal mAudioManagerInternal;
@Mock private SearchManager mSearchManager;
+ @Mock private RoleManager mRoleManager;
@Mock private Display mDisplay;
@Mock private DisplayRotation mDisplayRotation;
@@ -180,6 +184,9 @@ class TestPhoneWindowManager {
private boolean mIsTalkBackEnabled;
private boolean mIsTalkBackShortcutGestureEnabled;
+ private Intent mBrowserIntent;
+ private Intent mSmsIntent;
+
private int mKeyEventPolicyFlags = FLAG_INTERACTIVE;
private class TestTalkbackShortcutController extends TalkbackShortcutController {
@@ -290,6 +297,7 @@ class TestPhoneWindowManager {
doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
eq(SensorPrivacyManager.class));
doReturn(mSearchManager).when(mContext).getSystemService(eq(SearchManager.class));
+ doReturn(mRoleManager).when(mContext).getSystemService(eq(RoleManager.class));
doReturn(false).when(mPackageManager).hasSystemFeature(any());
try {
doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
@@ -360,6 +368,21 @@ class TestPhoneWindowManager {
doReturn(interceptionInfo)
.when(mWindowManagerInternal).getKeyInterceptionInfoFromToken(any());
+ doReturn(true).when(mRoleManager).isRoleAvailable(eq(RoleManager.ROLE_BROWSER));
+ doReturn(true).when(mRoleManager).isRoleAvailable(eq(RoleManager.ROLE_SMS));
+ doReturn(TEST_BROWSER_ROLE_PACKAGE_NAME).when(mRoleManager).getDefaultApplication(
+ eq(RoleManager.ROLE_BROWSER));
+ doReturn(TEST_SMS_ROLE_PACKAGE_NAME).when(mRoleManager).getDefaultApplication(
+ eq(RoleManager.ROLE_SMS));
+ mBrowserIntent = new Intent(Intent.ACTION_MAIN);
+ mBrowserIntent.setPackage(TEST_BROWSER_ROLE_PACKAGE_NAME);
+ mSmsIntent = new Intent(Intent.ACTION_MAIN);
+ mSmsIntent.setPackage(TEST_SMS_ROLE_PACKAGE_NAME);
+ doReturn(mBrowserIntent).when(mPackageManager).getLaunchIntentForPackage(
+ eq(TEST_BROWSER_ROLE_PACKAGE_NAME));
+ doReturn(mSmsIntent).when(mPackageManager).getLaunchIntentForPackage(
+ eq(TEST_SMS_ROLE_PACKAGE_NAME));
+
Mockito.reset(mContext);
}
@@ -670,6 +693,29 @@ class TestPhoneWindowManager {
Mockito.reset(mContext);
}
+ void assertLaunchRole(String role) {
+ mTestLooper.dispatchAll();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ try {
+ verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
+ switch (role) {
+ case RoleManager.ROLE_BROWSER:
+ Assert.assertEquals(intentCaptor.getValue(), mBrowserIntent);
+ break;
+ case RoleManager.ROLE_SMS:
+ Assert.assertEquals(intentCaptor.getValue(), mSmsIntent);
+ break;
+ default:
+ throw new AssertionError("Role " + role + " not supported in tests.");
+ }
+ } catch (Throwable t) {
+ throw new AssertionError("failed to assert " + role, t);
+ }
+ // Reset verifier for next call.
+ Mockito.reset(mContext);
+ }
+
+
void assertShowRecentApps() {
mTestLooper.dispatchAll();
verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 67c528cf40ae..09e7b9141e04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -527,7 +527,8 @@ public class ActivityRecordTests extends WindowTestsBase {
// The configuration change is still sent to the activity, even if it doesn't relaunch.
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
+ ActivityConfigurationChangeItem.obtain(activity.token, newConfig,
+ activity.getActivityWindowInfo());
verify(mClientLifecycleManager).scheduleTransactionItem(
eq(activity.app.getThread()), eq(expected));
}
@@ -599,7 +600,8 @@ public class ActivityRecordTests extends WindowTestsBase {
final Configuration currentConfig = activity.getConfiguration();
assertEquals(expectedOrientation, currentConfig.orientation);
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, currentConfig);
+ ActivityConfigurationChangeItem.obtain(activity.token, currentConfig,
+ activity.getActivityWindowInfo());
verify(mClientLifecycleManager).scheduleTransactionItem(activity.app.getThread(), expected);
verify(displayRotation).onSetRequestedOrientation();
}
@@ -818,7 +820,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token,
- activity.getConfiguration());
+ activity.getConfiguration(), activity.getActivityWindowInfo());
verify(mClientLifecycleManager).scheduleTransactionItem(
activity.app.getThread(), expected);
} finally {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 20bb549301aa..faa6d97ce0e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue;
import android.graphics.PixelFormat;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
+import android.view.inputmethod.ImeTracker;
import androidx.test.filters.SmallTest;
@@ -34,6 +35,12 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for the {@link ImeInsetsSourceProvider} class.
+ *
+ * <p> Build/Install/Run:
+ * atest WmTests:ImeInsetsSourceProviderTest
+ */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
@@ -56,7 +63,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mDisplayContent.setImeControlTarget(popup);
mDisplayContent.setImeLayeringTarget(appWin);
popup.mAttrs.format = PixelFormat.TRANSPARENT;
- mImeProvider.scheduleShowImePostLayout(appWin, null /* statsToken */);
+ mImeProvider.scheduleShowImePostLayout(appWin, ImeTracker.Token.empty());
assertTrue(mImeProvider.isReadyToShowIme());
}
@@ -65,7 +72,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
WindowState target = createWindow(null, TYPE_APPLICATION, "app");
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.updateImeInputAndControlTarget(target);
- mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */);
+ mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
assertTrue(mImeProvider.isReadyToShowIme());
}
@@ -79,7 +86,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.setImeControlTarget(target);
- mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */);
+ mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
assertFalse(mImeProvider.isImeShowing());
mImeProvider.checkShowImePostLayout();
assertTrue(mImeProvider.isImeShowing());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 60130635108c..da11e6ad613a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -554,7 +554,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
mRootWindowContainer.applySleepTokens(true);
// The display orientation should be changed by the activity so there is no relaunch.
- verify(activity, never()).relaunchActivityLocked(anyBoolean());
+ verify(activity, never()).relaunchActivityLocked(anyBoolean(), anyInt());
assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 01bd96b8a772..5360a1033eb4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -896,6 +896,11 @@ public class TaskFragmentTest extends WindowTestsBase {
assertFalse(mWm.moveFocusToTopEmbeddedWindow(winRightTop));
// The focus should NOT change.
assertEquals(winRightTop, mDisplayContent.mCurrentFocus);
+
+ // Do not move focus if the dim is boosted.
+ taskFragmentLeft.mDimmerSurfaceBoosted = true;
+ assertFalse(mWm.moveFocusToTopEmbeddedWindow(winLeftTop));
+ assertEquals(winRightTop, mDisplayContent.mCurrentFocus);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 7551b1650ad0..1233686a4b48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -265,7 +265,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
@Override
public boolean performHapticFeedback(int uid, String packageName, int effectId,
- boolean always, String reason) {
+ boolean always, String reason, boolean fromIme) {
return false;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index cd3ce9192509..c8ad4bd47880 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -104,6 +104,7 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
@@ -126,7 +127,7 @@ import java.util.List;
/**
* Tests for the {@link WindowState} class.
*
- * Build/Install/Run:
+ * <p> Build/Install/Run:
* atest WmTests:WindowStateTests
*/
@SmallTest
@@ -1099,7 +1100,7 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.setImeInputTarget(app);
app.setRequestedVisibleTypes(ime(), ime());
assertTrue(mDisplayContent.shouldImeAttachedToApp());
- controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */);
+ controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty());
controller.getImeSourceProvider().getSource().setVisible(true);
controller.updateAboveInsetsState(false);
@@ -1137,7 +1138,7 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.setImeInputTarget(app);
app.setRequestedVisibleTypes(ime(), ime());
assertTrue(mDisplayContent.shouldImeAttachedToApp());
- controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */);
+ controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty());
controller.getImeSourceProvider().getSource().setVisible(true);
controller.updateAboveInsetsState(false);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 44f4068fb424..883c702ddb79 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -383,7 +383,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage);
} else {
// Multiple packages means we need to go manual
- final int appId = UserHandle.getUserId(appInfo.uid);
+ final int appId = UserHandle.getAppId(appInfo.uid);
final String[] packageNames = new String[] { packageName };
final long[] ceDataInodes = new long[1];
String[] codePaths = new String[0];
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 530a39e8b53e..2da352d43290 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -48,20 +48,25 @@ import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbServiceDumpProto;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.widget.LockPatternUtils;
import com.android.server.FgThread;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
@@ -147,6 +152,8 @@ public class UsbService extends IUsbManager.Stub {
private final UsbSettingsManager mSettingsManager;
private final UsbPermissionManager mPermissionManager;
+ static final int PACKAGE_MONITOR_OPERATION_ID = 1;
+ static final int STRONG_AUTH_OPERATION_ID = 2;
/**
* The user id of the current user. There might be several profiles (with separate user ids)
* per user.
@@ -156,6 +163,10 @@ public class UsbService extends IUsbManager.Stub {
private final Object mLock = new Object();
+ // Key: USB port id
+ // Value: A set of UIDs of requesters who request disabling usb data
+ private final ArrayMap<String, ArraySet<Integer>> mUsbDisableRequesters = new ArrayMap<>();
+
/**
* @return the {@link UsbUserSettingsManager} for the given userId
*/
@@ -261,6 +272,14 @@ public class UsbService extends IUsbManager.Stub {
if (mDeviceManager != null) {
mDeviceManager.bootCompleted();
}
+ if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+ new PackageUninstallMonitor()
+ .register(mContext, UserHandle.ALL, BackgroundThread.getHandler());
+
+ new LockPatternUtils(mContext)
+ .registerStrongAuthTracker(new StrongAuthTracker(mContext,
+ BackgroundThread.getHandler().getLooper()));
+ }
}
/** Called when a user is unlocked. */
@@ -873,6 +892,11 @@ public class UsbService extends IUsbManager.Stub {
Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
+ operationId);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+ if (!shouldUpdateUsbSignaling(portId, enable, Binder.getCallingUid())) return false;
+ }
+
final long ident = Binder.clearCallingIdentity();
boolean wait;
try {
@@ -892,6 +916,31 @@ public class UsbService extends IUsbManager.Stub {
return wait;
}
+ /**
+ * If enable = true, exclude UID from update list.
+ * If enable = false, include UID in update list.
+ * Return false if enable = true and the list is empty (no updates).
+ * Return true otherwise (let downstream decide on updates).
+ */
+ private boolean shouldUpdateUsbSignaling(String portId, boolean enable, int uid) {
+ synchronized (mUsbDisableRequesters) {
+ if (!mUsbDisableRequesters.containsKey(portId)) {
+ mUsbDisableRequesters.put(portId, new ArraySet<>());
+ }
+
+ ArraySet<Integer> uidsOfDisableRequesters = mUsbDisableRequesters.get(portId);
+
+ if (enable) {
+ uidsOfDisableRequesters.remove(uid);
+ // re-enable USB port (return true) if there are no other disable requesters
+ return uidsOfDisableRequesters.isEmpty();
+ } else {
+ uidsOfDisableRequesters.add(uid);
+ }
+ }
+ return true;
+ }
+
@Override
public void enableUsbDataWhileDocked(String portId, int operationId,
IUsbOperationInternal callback) {
@@ -1344,4 +1393,55 @@ public class UsbService extends IUsbManager.Stub {
private static String removeLastChar(String value) {
return value.substring(0, value.length() - 1);
}
+
+ /**
+ * Upon app removal, clear associated UIDs from the mUsbDisableRequesters list
+ * and re-enable USB data signaling if no remaining apps require USB disabling.
+ */
+ private class PackageUninstallMonitor extends PackageMonitor {
+ @Override
+ public void onUidRemoved(int uid) {
+ synchronized (mUsbDisableRequesters) {
+ for (String portId : mUsbDisableRequesters.keySet()) {
+ ArraySet<Integer> disabledUid = mUsbDisableRequesters.get(portId);
+ if (disabledUid != null) {
+ disabledUid.remove(uid);
+ if (disabledUid.isEmpty()) {
+ enableUsbData(portId, true, PACKAGE_MONITOR_OPERATION_ID,
+ new IUsbOperationInternal.Default());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Implements a callback within StrongAuthTracker to disable USB data signaling
+ * when the device enters lockdown mode. This likely involves updating a state
+ * that controls USB data behavior.
+ */
+ private class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+ private boolean mLockdownModeStatus;
+
+ StrongAuthTracker(Context context, Looper looper) {
+ super(context, looper);
+ }
+
+ @Override
+ public synchronized void onStrongAuthRequiredChanged(int userId) {
+
+ boolean lockDownTriggeredByUser = (getStrongAuthForUser(userId)
+ & STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN) != 0;
+ //if it goes into the same lockdown status, no change is needed
+ if (mLockdownModeStatus == lockDownTriggeredByUser) {
+ return;
+ }
+ mLockdownModeStatus = lockDownTriggeredByUser;
+ for (UsbPort port: mPortManager.getPorts()) {
+ enableUsbData(port.getId(), !lockDownTriggeredByUser, STRONG_AUTH_OPERATION_ID,
+ new IUsbOperationInternal.Default());
+ }
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index b3db2dea4a27..862aff9be9ce 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -31,12 +31,11 @@ import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY;
import static android.provider.Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT;
-import static com.android.server.soundtrigger.SoundTriggerEvent.SessionEvent.Type;
-import static com.android.server.utils.EventLogger.Event.ALOGW;
-
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.soundtrigger.DeviceStateHandler.DeviceStateListener;
import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState;
+import static com.android.server.soundtrigger.SoundTriggerEvent.SessionEvent.Type;
+import static com.android.server.utils.EventLogger.Event.ALOGW;
import android.Manifest;
import android.annotation.NonNull;
@@ -94,8 +93,8 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.SparseArray;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ISoundTriggerService;
@@ -105,19 +104,17 @@ import com.android.server.SoundTriggerInternal;
import com.android.server.SystemService;
import com.android.server.soundtrigger.SoundTriggerEvent.ServiceEvent;
import com.android.server.soundtrigger.SoundTriggerEvent.SessionEvent;
-import com.android.server.utils.EventLogger.Event;
import com.android.server.utils.EventLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.function.Consumer;
-import java.util.List;
-import java.util.Set;
import java.util.Deque;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -126,6 +123,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -257,6 +255,11 @@ public class SoundTriggerService extends SystemService {
publishLocalService(SoundTriggerInternal.class, mLocalSoundTriggerService);
}
+ private boolean hasCalling() {
+ return mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING);
+ }
+
@Override
public void onBootPhase(int phase) {
Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode());
@@ -282,11 +285,13 @@ public class SoundTriggerService extends SystemService {
// Do so after registering the listener so we ensure that we don't drop any events
mDeviceStateHandler.onPowerModeChanged(powerManager.getSoundTriggerPowerSaveMode());
- // PhoneCallStateHandler initializes the original call state
- mPhoneCallStateHandler = new PhoneCallStateHandler(
- mContext.getSystemService(SubscriptionManager.class),
- mContext.getSystemService(TelephonyManager.class),
- mDeviceStateHandler);
+ if (hasCalling()) {
+ // PhoneCallStateHandler initializes the original call state
+ mPhoneCallStateHandler = new PhoneCallStateHandler(
+ mContext.getSystemService(SubscriptionManager.class),
+ mContext.getSystemService(TelephonyManager.class),
+ mDeviceStateHandler);
+ }
}
mMiddlewareService = ISoundTriggerMiddlewareService.Stub.asInterface(
ServiceManager.waitForService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE));
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index aef7158fd613..5a5296836089 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -66,7 +66,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PermissionEnforcer;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
@@ -76,7 +75,6 @@ import android.os.SharedMemory;
import android.os.ShellCallback;
import android.os.Trace;
import android.os.UserHandle;
-import android.permission.flags.Flags;
import android.provider.Settings;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
@@ -181,10 +179,8 @@ public class VoiceInteractionManagerService extends SystemService {
LocalServices.getService(ActivityManagerInternal.class));
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
- mWmInternal = Objects.requireNonNull(
- LocalServices.getService(WindowManagerInternal.class));
- mDpmInternal = Objects.requireNonNull(
- LocalServices.getService(DevicePolicyManagerInternal.class));
+ mWmInternal = LocalServices.getService(WindowManagerInternal.class);
+ mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
permissionManagerInternal.setVoiceInteractionPackagesProvider(
@@ -1409,17 +1405,6 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- // Enforce permissions that are flag controlled. The flag value decides if the permission
- // should be enforced.
- private void initAndVerifyDetector_enforcePermissionWithFlags() {
- PermissionEnforcer enforcer = mContext.getSystemService(PermissionEnforcer.class);
- if (Flags.voiceActivationPermissionApis()) {
- enforcer.enforcePermission(
- android.Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO,
- getCallingPid(), getCallingUid());
- }
- }
-
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
@Override
public void initAndVerifyDetector(
@@ -1429,13 +1414,7 @@ public class VoiceInteractionManagerService extends SystemService {
@NonNull IBinder token,
IHotwordRecognitionStatusCallback callback,
int detectorType) {
- // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
- // {@link #initAndVerifyDetector(Identity, PersistableBundle, ShareMemory, IBinder,
- // IHotwordRecognitionStatusCallback, int)}
- // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully
- // launched.
super.initAndVerifyDetector_enforcePermission();
- initAndVerifyDetector_enforcePermissionWithFlags();
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
@@ -2750,11 +2729,17 @@ public class VoiceInteractionManagerService extends SystemService {
if (isAssistDataAllowed) {
visiblePackageNames.add(record.getComponentName().getPackageName());
}
- if (mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
+ if (mDpmInternal != null
+ && mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
isManagedProfileVisible = true;
}
}
- final ScreenCapture.ScreenshotHardwareBuffer shb = mWmInternal.takeAssistScreenshot();
+ final ScreenCapture.ScreenshotHardwareBuffer shb;
+ if (mWmInternal != null) {
+ shb = mWmInternal.takeAssistScreenshot();
+ } else {
+ shb = null;
+ }
final Bitmap bm = shb != null ? shb.asBitmap() : null;
// Now that everything is fetched, putting it in the launchIntent.
if (bm != null) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 2fc6a22261b6..8b503f2b97e4 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -28,6 +28,8 @@ import com.android.internal.telecom.IConnectionService;
import com.android.internal.telecom.IConnectionServiceAdapter;
import com.android.internal.telecom.IVideoProvider;
import com.android.internal.telecom.RemoteServiceCallback;
+import com.android.server.telecom.flags.FeatureFlags;
+import com.android.server.telecom.flags.FeatureFlagsImpl;
import java.util.ArrayList;
import java.util.HashMap;
@@ -550,6 +552,9 @@ final class RemoteConnectionService {
private final Map<String, RemoteConference> mConferenceById = new HashMap<>();
private final Set<RemoteConnection> mPendingConnections = new HashSet<>();
+ /** Telecom feature flags **/
+ private final FeatureFlags mTelecomFeatureFlags = new FeatureFlagsImpl();
+
RemoteConnectionService(
IConnectionService outgoingConnectionServiceRpc,
ConnectionService ourConnectionServiceImpl) throws RemoteException {
@@ -578,6 +583,14 @@ final class RemoteConnectionService {
extras.putString(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
mOurConnectionServiceImpl.getApplicationContext().getOpPackageName());
+ // Defaulted ConnectionRequest params
+ String telecomCallId = "";
+ boolean shouldShowIncomingUI = false;
+ if (mTelecomFeatureFlags.setRemoteConnectionCallId()) {
+ telecomCallId = id;
+ shouldShowIncomingUI = request.shouldShowIncomingCallUi();
+ }
+
final ConnectionRequest newRequest = new ConnectionRequest.Builder()
.setAccountHandle(request.getAccountHandle())
.setAddress(request.getAddress())
@@ -585,6 +598,9 @@ final class RemoteConnectionService {
.setVideoState(request.getVideoState())
.setRttPipeFromInCall(request.getRttPipeFromInCall())
.setRttPipeToInCall(request.getRttPipeToInCall())
+ // Flagged changes
+ .setTelecomCallId(telecomCallId)
+ .setShouldShowIncomingCallUi(shouldShowIncomingUI)
.build();
try {
if (mConnectionById.isEmpty()) {
@@ -626,10 +642,28 @@ final class RemoteConnectionService {
mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub(),
null /*Session.Info*/);
}
+
+ // Set telecom call id to what's being tracked by base ConnectionService.
+ String telecomCallId = mTelecomFeatureFlags.setRemoteConnectionCallId()
+ ? id : request.getTelecomCallId();
+
+ final ConnectionRequest newRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(request.getAccountHandle())
+ .setAddress(request.getAddress())
+ .setExtras(request.getExtras())
+ .setVideoState(request.getVideoState())
+ .setShouldShowIncomingCallUi(request.shouldShowIncomingCallUi())
+ .setRttPipeFromInCall(request.getRttPipeFromInCall())
+ .setRttPipeToInCall(request.getRttPipeToInCall())
+ .setParticipants(request.getParticipants())
+ .setIsAdhocConferenceCall(request.isAdhocConferenceCall())
+ .setTelecomCallId(telecomCallId)
+ .build();
+
RemoteConference conference = new RemoteConference(id, mOutgoingConnectionServiceRpc);
mOutgoingConnectionServiceRpc.createConference(connectionManagerPhoneAccount,
id,
- request,
+ newRequest,
isIncoming,
false /* isUnknownCall */,
null /*Session.info*/);
@@ -640,7 +674,7 @@ final class RemoteConnectionService {
maybeDisconnectAdapter();
}
});
- conference.putExtras(request.getExtras());
+ conference.putExtras(newRequest.getExtras());
return conference;
} catch (RemoteException e) {
return RemoteConference.failure(
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9792cdd80a00..048b1b290dde 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1434,11 +1434,14 @@ public class TelecomManager {
}
/**
- * This API will return all {@link PhoneAccount}s registered via
- * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears
- * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount}
- * or the caller registered the {@link PhoneAccount} under a different user and does not
- * have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ * This API will return all {@link PhoneAccount}s the caller registered via
+ * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears
+ * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount} (for
+ * cleanup purposes) or the caller registered the {@link PhoneAccount} under a different user
+ * and does not have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ * <b>Note:</b> This API will only return {@link PhoneAccount}s registered by the same app. For
+ * system Dialers that need all the {@link PhoneAccount}s registered by every application, see
+ * {@link TelecomManager#getAllPhoneAccounts()}.
*
* @return all the {@link PhoneAccount}s registered by the caller.
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5d99acd87dd3..2150b5deff52 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5307,6 +5307,19 @@ public class CarrierConfigManager {
KEY_PREFIX + "enable_presence_group_subscribe_bool";
/**
+ * SIP SUBSCRIBE retry duration used when device doesn't receive a response to SIP
+ * SUBSCRIBE request.
+ * If this value is not defined or defined as negative value, the device does not retry
+ * the SIP SUBSCRIBE.
+ * If the value is 0 then device retries immediately upon timeout.
+ * If the value is > 0 then device waits for configured duration and retries after timeout
+ * is detected
+ * @hide
+ */
+ public static final String KEY_SUBSCRIBE_RETRY_DURATION_MILLIS_LONG =
+ KEY_PREFIX + "subscribe_retry_duration_millis_long";
+
+ /**
* Flag indicating whether or not to use SIP URI when send a presence subscribe.
* When {@code true}, the device sets the To and Contact header to be SIP URI using
* the TelephonyManager#getIsimDomain" API.
@@ -5982,6 +5995,7 @@ public class CarrierConfigManager {
defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, false);
+ defaults.putInt(KEY_SUBSCRIBE_RETRY_DURATION_MILLIS_LONG, -1);
defaults.putBoolean(KEY_USE_SIP_URI_FOR_PRESENCE_SUBSCRIBE_BOOL, false);
defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
defaults.putBoolean(KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 82ed340e8a0c..58488d1900fe 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -573,7 +573,7 @@ public class SubscriptionInfo implements Parcelable {
* Returns the number of this subscription.
*
* Starting with API level 30, returns the number of this subscription if the calling app meets
- * one of the following requirements:
+ * at least one of the following requirements:
* <ul>
* <li>If the calling app's target SDK is API level 29 or lower and the app has been granted
* the READ_PHONE_STATE permission.
@@ -584,8 +584,8 @@ public class SubscriptionInfo implements Parcelable {
* <li>If the calling app is the default SMS role holder.
* </ul>
*
- * @return the number of this subscription, or an empty string if one of these requirements is
- * not met
+ * @return the number of this subscription, or an empty string if none of the requirements
+ * are met.
* @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
* {@link #getSubscriptionId() subscription ID}.
*/
diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
index c2f5b8f3e7da..901daf813e86 100644
--- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
+++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
@@ -19,12 +19,14 @@ package android.telephony;
import android.annotation.NonNull;
import android.app.SystemServiceRegistry;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.TelephonyServiceManager;
import android.telephony.euicc.EuiccCardManager;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsManager;
import android.telephony.satellite.SatelliteManager;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
@@ -55,6 +57,11 @@ public class TelephonyFrameworkInitializer {
sTelephonyServiceManager = Preconditions.checkNotNull(telephonyServiceManager);
}
+ private static boolean hasSystemFeature(Context context, String feature) {
+ if (!Flags.minimalTelephonyManagersConditionalOnFeatures()) return true;
+ return context.getPackageManager().hasSystemFeature(feature);
+ }
+
/**
* Called by {@link SystemServiceRegistry}'s static initializer and registers all telephony
* services to {@link Context}, so that {@link Context#getSystemService} can return them.
@@ -76,33 +83,39 @@ public class TelephonyFrameworkInitializer {
SystemServiceRegistry.registerContextAwareService(
Context.CARRIER_CONFIG_SERVICE,
CarrierConfigManager.class,
- context -> new CarrierConfigManager(context)
+ context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ ? new CarrierConfigManager(context) : null
);
SystemServiceRegistry.registerContextAwareService(
Context.EUICC_SERVICE,
EuiccManager.class,
- context -> new EuiccManager(context)
+ context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_EUICC)
+ ? new EuiccManager(context) : null
);
SystemServiceRegistry.registerContextAwareService(
Context.EUICC_CARD_SERVICE,
EuiccCardManager.class,
- context -> new EuiccCardManager(context)
+ context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_EUICC)
+ ? new EuiccCardManager(context) : null
);
SystemServiceRegistry.registerContextAwareService(
Context.TELEPHONY_IMS_SERVICE,
ImsManager.class,
- context -> new ImsManager(context)
+ context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_IMS)
+ ? new ImsManager(context) : null
);
SystemServiceRegistry.registerContextAwareService(
Context.SMS_SERVICE,
SmsManager.class,
- context -> SmsManager.getSmsManagerForContextAndSubscriptionId(context,
- SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
+ context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_MESSAGING)
+ ? SmsManager.getSmsManagerForContextAndSubscriptionId(context,
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) : null
);
SystemServiceRegistry.registerContextAwareService(
Context.SATELLITE_SERVICE,
SatelliteManager.class,
- context -> new SatelliteManager(context)
+ context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_SATELLITE)
+ ? new SatelliteManager(context) : null
);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 626a2e574881..88acbabc0e0f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8435,7 +8435,7 @@ public class TelephonyManager {
* In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of this
* API must also be listed in the device configuration as an authorized app in
* {@code packages/services/Telephony/res/values/config.xml} under the
- * {@code config_number_verification_package_name} key.
+ * {@code platform_number_verification_package} key.
*
* @hide
* @param range The range of phone numbers the caller expects a phone call from.
@@ -15023,7 +15023,7 @@ public class TelephonyManager {
@FlaggedApi(android.permission.flags.Flags.FLAG_GET_EMERGENCY_ROLE_HOLDER_API_ENABLED)
@NonNull
@SystemApi
- public String getEmergencyAssistancePackage() {
+ public String getEmergencyAssistancePackageName() {
if (!isEmergencyAssistanceEnabled() || !isVoiceCapable()) {
throw new IllegalStateException("isEmergencyAssistanceEnabled() is false or device"
+ " not voice capable.");
@@ -18535,7 +18535,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_GET_LAST_KNOWN_CELL_IDENTITY)
@RequiresPermission(allOf = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID})
public @Nullable CellIdentity getLastKnownCellIdentity() {
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index 07e3f8736cc8..b2a8847039c7 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -16,9 +16,7 @@
package android.test;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.internal.util.Predicate;
+import static android.test.suitebuilder.TestPredicates.hasAnnotation;
import android.app.Activity;
import android.app.Instrumentation;
@@ -29,16 +27,11 @@ import android.test.suitebuilder.TestMethod;
import android.test.suitebuilder.TestPredicates;
import android.test.suitebuilder.TestSuiteBuilder;
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.PrintStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.internal.util.Predicate;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
@@ -49,7 +42,14 @@ import junit.framework.TestSuite;
import junit.runner.BaseTestRunner;
import junit.textui.ResultPrinter;
-import static android.test.suitebuilder.TestPredicates.hasAnnotation;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
/**
* An {@link Instrumentation} that runs various types of {@link junit.framework.TestCase}s against
diff --git a/test-runner/src/android/test/suitebuilder/TestPredicates.java b/test-runner/src/android/test/suitebuilder/TestPredicates.java
index 616d1a972612..faf31fdc5350 100644
--- a/test-runner/src/android/test/suitebuilder/TestPredicates.java
+++ b/test-runner/src/android/test/suitebuilder/TestPredicates.java
@@ -19,7 +19,9 @@ package android.test.suitebuilder;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.Smoke;
import android.test.suitebuilder.annotation.Suppress;
+
import com.android.internal.util.Predicate;
+
import java.lang.annotation.Annotation;
/**
diff --git a/test-runner/tests/src/android/test/AndroidTestRunnerTest.java b/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
index 67235480fa6d..bd6c04bca4d0 100644
--- a/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
+++ b/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
@@ -19,15 +19,15 @@ package android.test;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
-import java.util.ArrayList;
-import junit.framework.TestCase;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
-import junit.framework.TestSuite;
+import junit.framework.TestCase;
import junit.framework.TestListener;
+import junit.framework.TestSuite;
-import java.util.List;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* Unit tests for {@link AndroidTestRunner}
diff --git a/tests/CoreTests/android/Android.bp b/tests/CoreTests/android/Android.bp
index e2f194b04437..97a6e5fd4d1b 100644
--- a/tests/CoreTests/android/Android.bp
+++ b/tests/CoreTests/android/Android.bp
@@ -16,5 +16,8 @@ android_test {
"android.test.base.stubs",
],
sdk_version: "current",
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "androidx.test.rules",
+ ],
}
diff --git a/tests/CoreTests/android/core/RequestAPITest.java b/tests/CoreTests/android/core/RequestAPITest.java
index 206f228c3804..2d420d30b299 100644
--- a/tests/CoreTests/android/core/RequestAPITest.java
+++ b/tests/CoreTests/android/core/RequestAPITest.java
@@ -19,10 +19,11 @@ package android.core;
import android.net.http.RequestHandle;
import android.net.http.RequestQueue;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
import android.webkit.CookieSyncManager;
+import androidx.test.filters.Suppress;
+
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap;
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 1d71f95ef64f..d658d5991a57 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -63,17 +63,20 @@ java_library {
],
}
-android_library_import {
- name: "wm-flicker-window-extensions_nodeps",
- aars: ["libs/window-extensions-release.aar"],
+java_library {
+ name: "wm-flicker-window-extensions",
sdk_version: "current",
+ static_libs: [
+ "androidx.window.extensions_extensions-nodeps",
+ ],
+ installable: false,
}
java_library {
- name: "wm-flicker-window-extensions",
+ name: "wm-flicker-window-extensions-core",
sdk_version: "current",
static_libs: [
- "wm-flicker-window-extensions_nodeps",
+ "androidx.window.extensions.core_core-nodeps",
],
installable: false,
}
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index d47329ea97b4..57da05f13bbb 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
@@ -52,6 +52,7 @@ class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) :
// Can't use TAPL due to Recents not showing in 3 Button Nav in full screen mode
device.pressHome()
tapl.getWorkspace()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
}
teardown { testApp.exit(wmHelper) }
transitions { testApp.launchViaIntent(wmHelper) }
diff --git a/tests/FlickerTests/libs/window-extensions-release.aar b/tests/FlickerTests/libs/window-extensions-release.aar
deleted file mode 100644
index 918e514f4c89..000000000000
--- a/tests/FlickerTests/libs/window-extensions-release.aar
+++ /dev/null
Binary files differ
diff --git a/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java b/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java
index d6e2861c03a7..cc0255d4140a 100644
--- a/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java
+++ b/tests/GamePerformance/src/android/gameperformance/GamePerformanceTest.java
@@ -15,6 +15,14 @@
*/
package android.gameperformance;
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.os.Bundle;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -22,18 +30,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Trace;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
public class GamePerformanceTest extends
ActivityInstrumentationTestCase2<GamePerformanceActivity> {
private final static String TAG = "GamePerformanceTest";
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 7343ba1c1ce7..e60764f137af 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
+import android.hardware.input.KeyboardLayoutSelectionResult
import android.hardware.input.IInputManager
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
@@ -525,13 +526,13 @@ class KeyboardLayoutManagerTests {
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
ENGLISH_UK_LAYOUT_DESCRIPTOR
)
- val keyboardLayout =
+ assertEquals(
+ "Default UI: getKeyboardLayoutForInputDevice API should always return " +
+ "KeyboardLayoutSelectionResult.FAILED",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
- assertNull(
- "Default UI: getKeyboardLayoutForInputDevice API should always return null",
- keyboardLayout
)
}
}
@@ -545,12 +546,14 @@ class KeyboardLayoutManagerTests {
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
ENGLISH_UK_LAYOUT_DESCRIPTOR
)
- assertEquals(
- "New UI: getKeyboardLayoutForInputDevice API should return the set layout",
- ENGLISH_UK_LAYOUT_DESCRIPTOR,
+ var result =
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
+ assertEquals(
+ "New UI: getKeyboardLayoutForInputDevice API should return the set layout",
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
+ result.layoutDescriptor
)
// This should replace previously set layout
@@ -558,12 +561,14 @@ class KeyboardLayoutManagerTests {
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
ENGLISH_US_LAYOUT_DESCRIPTOR
)
- assertEquals(
- "New UI: getKeyboardLayoutForInputDevice API should return the last set layout",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
+ result =
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
+ assertEquals(
+ "New UI: getKeyboardLayoutForInputDevice API should return the last set layout",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ result.layoutDescriptor
)
}
}
@@ -734,17 +739,20 @@ class KeyboardLayoutManagerTests {
createImeSubtypeForLanguageTag("ru"),
createLayoutDescriptor("keyboard_layout_russian")
)
- assertNull(
- "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " +
- "layout available",
+ assertEquals(
+ "New UI: getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout available",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
createImeSubtypeForLanguageTag("it")
)
)
- assertNull(
- "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " +
- "layout for script code is available",
+ assertEquals(
+ "New UI: getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
+ "available",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
createImeSubtypeForLanguageTag("en-Deva")
@@ -811,8 +819,10 @@ class KeyboardLayoutManagerTests {
createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
createLayoutDescriptor("keyboard_layout_russian")
)
- assertNull("New UI: getDefaultKeyboardLayoutForInputDevice should return null when " +
- "no layout for script code is available",
+ assertEquals("New UI: getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
+ "available",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "")
@@ -865,14 +875,16 @@ class KeyboardLayoutManagerTests {
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.eq(keyboardDevice.vendorId),
ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(createByteArray(
+ ArgumentMatchers.eq(
+ createByteArray(
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
LAYOUT_TYPE_DEFAULT,
GERMAN_LAYOUT_NAME,
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
"de-Latn",
- LAYOUT_TYPE_QWERTZ),
+ LAYOUT_TYPE_QWERTZ
),
+ ),
ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
@@ -893,13 +905,16 @@ class KeyboardLayoutManagerTests {
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
- ArgumentMatchers.eq(createByteArray(
+ ArgumentMatchers.eq(
+ createByteArray(
"en",
LAYOUT_TYPE_QWERTY,
ENGLISH_US_LAYOUT_NAME,
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
"de-Latn",
- LAYOUT_TYPE_QWERTZ)),
+ LAYOUT_TYPE_QWERTZ
+ )
+ ),
ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
@@ -918,14 +933,16 @@ class KeyboardLayoutManagerTests {
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.eq(keyboardDevice.vendorId),
ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(createByteArray(
+ ArgumentMatchers.eq(
+ createByteArray(
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
LAYOUT_TYPE_DEFAULT,
"Default",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT,
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT),
+ LAYOUT_TYPE_DEFAULT
),
+ ),
ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
@@ -998,12 +1015,13 @@ class KeyboardLayoutManagerTests {
imeSubtype: InputMethodSubtype,
expectedLayout: String
) {
+ val result = keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ device.identifier, USER_ID, imeInfo, imeSubtype
+ )
assertEquals(
"New UI: getDefaultKeyboardLayoutForInputDevice should return $expectedLayout",
expectedLayout,
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- device.identifier, USER_ID, imeInfo, imeSubtype
- )
+ result.layoutDescriptor
)
}
diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
index 89a47b9b736a..0615941eda09 100644
--- a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -17,6 +17,7 @@
package com.android.server.input
import android.hardware.input.KeyboardLayout
+import android.hardware.input.KeyboardLayoutSelectionResult
import android.icu.util.ULocale
import android.platform.test.annotations.Presubmit
import android.view.InputDevice
@@ -120,15 +121,15 @@ class KeyboardMetricsCollectorTests {
val event = builder.addLayoutSelection(
createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
"English(US)(Qwerty)",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
).addLayoutSelection(
createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"),
null, // Default layout type
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER
).addLayoutSelection(
createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"),
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
).setIsFirstTimeConfiguration(true).build()
assertEquals(
@@ -158,7 +159,7 @@ class KeyboardMetricsCollectorTests {
"de-CH",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
"English(US)(Qwerty)",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
"en-US",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
)
@@ -167,7 +168,7 @@ class KeyboardMetricsCollectorTests {
"de-CH",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
KeyboardMetricsCollector.DEFAULT_LAYOUT_NAME,
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER,
"en-US",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
)
@@ -176,7 +177,7 @@ class KeyboardMetricsCollectorTests {
"de-CH",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
"en-US",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
)
@@ -197,7 +198,7 @@ class KeyboardMetricsCollectorTests {
val event = builder.addLayoutSelection(
createImeSubtype(4, null, "qwerty"), // Default language tag
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
).build()
assertExpectedLayoutConfiguration(
@@ -205,7 +206,7 @@ class KeyboardMetricsCollectorTests {
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
)
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
index b9f1738f9bb7..a96389046d6a 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
@@ -37,7 +37,7 @@ import perfetto.protos.ProtologConfig;
public class PerfettoDataSourceTest {
@Before
public void before() {
- assumeTrue(android.tracing.Flags.perfettoProtolog());
+ assumeTrue(android.tracing.Flags.perfettoProtologTracing());
}
@Test
diff --git a/tests/SurfaceComposition/Android.bp b/tests/SurfaceComposition/Android.bp
index f5aba8f5a2f2..a02662fb912e 100644
--- a/tests/SurfaceComposition/Android.bp
+++ b/tests/SurfaceComposition/Android.bp
@@ -32,7 +32,10 @@ android_test {
enabled: false,
},
srcs: ["src/**/*.java"],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "androidx.test.rules",
+ ],
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 261ea2ec866d..f82585c02c08 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -22,9 +22,10 @@ import android.os.Bundle;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.AllocationScore;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.CompositorScore;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import androidx.test.filters.SmallTest;
+
public class SurfaceCompositionTest extends
ActivityInstrumentationTestCase2<SurfaceCompositionMeasuringActivity> {
private final static String TAG = "SurfaceCompositionTest";
diff --git a/tests/graphics/HwAccelerationTest/jni/native-lib.cpp b/tests/graphics/HwAccelerationTest/jni/native-lib.cpp
index 407d4bf76336..2977c2157261 100644
--- a/tests/graphics/HwAccelerationTest/jni/native-lib.cpp
+++ b/tests/graphics/HwAccelerationTest/jni/native-lib.cpp
@@ -30,7 +30,7 @@ struct MyWrapper {
void setBuffer(AHardwareBuffer* buffer) {
ASurfaceTransaction* transaction = ASurfaceTransaction_create();
- ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer);
+ ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer, -1);
ASurfaceTransaction_setVisibility(transaction, surfaceControl,
ASURFACE_TRANSACTION_VISIBILITY_SHOW);
ASurfaceTransaction_apply(transaction);
diff --git a/tests/permission/Android.bp b/tests/permission/Android.bp
index d06809b209a0..b02f410c733e 100644
--- a/tests/permission/Android.bp
+++ b/tests/permission/Android.bp
@@ -20,6 +20,7 @@ android_test {
"androidx.test.runner",
"junit",
"platform-test-annotations",
+ "androidx.test.rules",
],
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
index 5fb23b0ad507..99ca9c084b54 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
@@ -21,7 +21,8 @@ import android.app.ActivityTaskManager;
import android.app.IActivityManager;
import android.content.res.Configuration;
import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import junit.framework.TestCase;
diff --git a/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java
index 299d8d003d9c..0c9d447a3ac1 100644
--- a/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java
@@ -19,7 +19,8 @@ package com.android.framework.permission.tests;
import android.app.PackageInstallObserver;
import android.content.pm.PackageManager;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
/**
* Verify PackageManager api's that require specific permissions.
diff --git a/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java
index 41727431bd5e..2fa0b2164835 100644
--- a/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java
@@ -18,7 +18,8 @@ package com.android.framework.permission.tests;
import android.telephony.SmsManager;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import java.util.ArrayList;
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 330bc84d7a76..e67fd6702f7b 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -23,9 +23,10 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.IWindowManager;
+import androidx.test.filters.SmallTest;
+
import junit.framework.TestCase;
/**
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index a02eb6b176dc..fd5c4caca484 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -30,10 +30,11 @@ import static org.mockito.Mockito.when;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableLooper.MessageHandler;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.filters.SmallTest;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/testables/tests/src/android/testing/TestableResourcesTest.java b/tests/testables/tests/src/android/testing/TestableResourcesTest.java
index dd4325c59aa2..77916944eeea 100644
--- a/tests/testables/tests/src/android/testing/TestableResourcesTest.java
+++ b/tests/testables/tests/src/android/testing/TestableResourcesTest.java
@@ -21,9 +21,9 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import android.content.res.Resources;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import com.android.testables.R;
diff --git a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
index 0333d514343d..f3f429a39c3c 100644
--- a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
+++ b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
@@ -20,9 +20,9 @@ import android.content.ContentResolver;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
diff --git a/tests/utils/testutils/tests/Android.bp b/tests/utils/testutils/tests/Android.bp
index b901b1802383..8104280cdd5e 100644
--- a/tests/utils/testutils/tests/Android.bp
+++ b/tests/utils/testutils/tests/Android.bp
@@ -31,6 +31,7 @@ android_test {
"androidx.test.runner",
"mockito-target-minus-junit4",
"frameworks-base-testutils",
+ "androidx.test.rules",
],
libs: [
diff --git a/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
index c72e20ccc5ba..6205b98d4e79 100644
--- a/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
+++ b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
@@ -28,7 +28,8 @@ import static org.mockito.Mockito.spy;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Rule;
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index b054a57e85af..b4e2758f4abe 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -15,12 +15,7 @@
//
package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
toolSources = [
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
deleted file mode 100644
index 15ae2baa73df..000000000000
--- a/tools/aapt2/Android.mk
+++ /dev/null
@@ -1,4 +0,0 @@
-include $(CLEAR_VARS)
-aapt2_results := ./out/soong/.intermediates/frameworks/base/tools/aapt2/aapt2_results
-$(call declare-1p-target,$(aapt2_results))
-aapt2_results :=
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
index 83e09bf7043e..fd7474b55fa6 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
@@ -20,7 +20,7 @@ import com.android.hoststubgen.asm.toJvmClassName
/**
* Filter to apply a policy to classes extending or implementing a class,
- * either directly or indirectly. (with a breadth first search.)
+ * either directly or indirectly.
*
* The policy won't apply to the super class itself.
*/
@@ -42,7 +42,7 @@ class SubclassFilter(
}
/**
- * Find a policy for a class with a breadth-first search.
+ * Find a policy for a class.
*/
private fun findPolicyForClass(className: String): FilterPolicyWithReason? {
val cn = classes.findClass(className) ?: return null
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index cee29dcd1d59..1dec6ab092cb 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -46,6 +46,7 @@ def check_one_file(filename):
class TestWithGoldenOutput(unittest.TestCase):
# Test to check the generated jar files to the golden output.
+ @unittest.skip("Disabled until JDK 21 is merged and the golden files updated")
def test_compare_to_golden(self):
files = os.listdir(GOLDEN_DIR)
files.sort()
diff --git a/tools/protologtool/src/com/android/protolog/tool/Constants.kt b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
index aa3e00f2f4db..4a93de916826 100644
--- a/tools/protologtool/src/com/android/protolog/tool/Constants.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
@@ -18,7 +18,7 @@ package com.android.protolog.tool
object Constants {
const val NAME = "protologtool"
- const val VERSION = "1.0.0"
+ const val VERSION = "2.0.0"
const val IS_ENABLED_METHOD = "isEnabled"
const val ENUM_VALUES_METHOD = "values"
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 1381847c258f..ea2c8dacff50 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -39,8 +39,7 @@ import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
-import kotlin.math.abs
-import kotlin.random.Random
+import kotlin.math.absoluteValue
import kotlin.system.exitProcess
object ProtoLogTool {
@@ -72,7 +71,11 @@ object ProtoLogTool {
}
private fun processClasses(command: CommandOptions) {
- val generationHash = abs(Random.nextInt())
+ // A deterministic hash based on the group jar path and the source files we are processing.
+ // The hash is required to make sure different ProtoLogImpls don't conflict.
+ val generationHash = (command.javaSourceArgs.toTypedArray() + command.protoLogGroupsJarArg)
+ .contentHashCode().absoluteValue
+
// Need to generate a new impl class to inject static constants into the class.
val generatedProtoLogImplClass =
"com.android.internal.protolog.ProtoLogImpl_$generationHash"
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index b18bdff7263f..b1b314fcdb19 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -17,6 +17,7 @@
// ==========================================================
// Build the host executable: protoc-gen-javastream
// ==========================================================
+
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -41,6 +42,32 @@ cc_defaults {
static_libs: ["libprotoc"],
}
+// ==========================================================
+// Build the host static library: java_streaming_proto_lib
+// ==========================================================
+
+cc_library_host_static {
+ name: "java_streaming_proto_lib",
+ defaults: ["protoc-gen-stream-defaults"],
+ target: {
+ darwin: {
+ cflags: ["-D_DARWIN_UNLIMITED_STREAMS"],
+ },
+ },
+ cflags: [
+ "-Wno-format-y2k",
+ "-DSTATIC_ANDROIDFW_FOR_TOOLS",
+ ],
+
+ srcs: [
+ "java/java_proto_stream_code_generator.cpp",
+ ],
+}
+
+// ==========================================================
+// Build the host executable: protoc-gen-javastream
+// ==========================================================
+
cc_binary_host {
name: "protoc-gen-javastream",
srcs: [
@@ -48,8 +75,13 @@ cc_binary_host {
],
defaults: ["protoc-gen-stream-defaults"],
+ static_libs: ["java_streaming_proto_lib"],
}
+// ==========================================================
+// Build the host executable: protoc-gen-cppstream
+// ==========================================================
+
cc_binary_host {
name: "protoc-gen-cppstream",
srcs: [
@@ -60,13 +92,31 @@ cc_binary_host {
}
// ==========================================================
+// Build the host tests: StreamingProtoTest
+// ==========================================================
+
+cc_test_host {
+ name: "StreamingProtoTest",
+ defaults: ["protoc-gen-stream-defaults"],
+ srcs: [
+ "test/unit/**/*.cpp",
+ ],
+ static_libs: [
+ "java_streaming_proto_lib",
+ "libgmock",
+ "libgtest",
+ ],
+}
+
+// ==========================================================
// Build the java test
// ==========================================================
+
java_library {
- name: "StreamingProtoTest",
+ name: "StreamingProtoJavaIntegrationTest",
srcs: [
- "test/**/*.java",
- "test/**/*.proto",
+ "test/integration/**/*.java",
+ "test/integration/**/*.proto",
],
proto: {
type: "stream",
diff --git a/tools/streaming_proto/java/java_proto_stream_code_generator.cpp b/tools/streaming_proto/java/java_proto_stream_code_generator.cpp
new file mode 100644
index 000000000000..9d61111fb5bd
--- /dev/null
+++ b/tools/streaming_proto/java/java_proto_stream_code_generator.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#include "java_proto_stream_code_generator.h"
+
+#include <stdio.h>
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "Errors.h"
+
+using namespace android::stream_proto;
+using namespace google::protobuf::io;
+using namespace std;
+
+/**
+ * If the descriptor gives us a class name, use that. Otherwise make one up from
+ * the filename of the .proto file.
+ */
+static string make_outer_class_name(const FileDescriptorProto& file_descriptor) {
+ string name = file_descriptor.options().java_outer_classname();
+ if (name.size() == 0) {
+ name = to_camel_case(file_base_name(file_descriptor.name()));
+ if (name.size() == 0) {
+ ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE,
+ "Unable to make an outer class name for file: %s",
+ file_descriptor.name().c_str());
+ name = "Unknown";
+ }
+ }
+ return name;
+}
+
+/**
+ * Figure out the package name that we are generating.
+ */
+static string make_java_package(const FileDescriptorProto& file_descriptor) {
+ if (file_descriptor.options().has_java_package()) {
+ return file_descriptor.options().java_package();
+ } else {
+ return file_descriptor.package();
+ }
+}
+
+/**
+ * Figure out the name of the file we are generating.
+ */
+static string make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name) {
+ string const package = make_java_package(file_descriptor);
+ string result;
+ if (package.size() > 0) {
+ result = replace_string(package, '.', '/');
+ result += '/';
+ }
+
+ result += class_name;
+ result += ".java";
+
+ return result;
+}
+
+static string indent_more(const string& indent) {
+ return indent + INDENT;
+}
+
+/**
+ * Write the constants for an enum.
+ */
+static void write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent) {
+ const int N = enu.value_size();
+ text << indent << "// enum " << enu.name() << endl;
+ for (int i = 0; i < N; i++) {
+ const EnumValueDescriptorProto& value = enu.value(i);
+ text << indent << "public static final int " << make_constant_name(value.name()) << " = "
+ << value.number() << ";" << endl;
+ }
+ text << endl;
+}
+
+/**
+ * Write a field.
+ */
+static void write_field(stringstream& text, const FieldDescriptorProto& field,
+ const string& indent) {
+ string optional_comment =
+ field.label() == FieldDescriptorProto::LABEL_OPTIONAL ? "optional " : "";
+ string repeated_comment =
+ field.label() == FieldDescriptorProto::LABEL_REPEATED ? "repeated " : "";
+ string proto_type = get_proto_type(field);
+ string packed_comment = field.options().packed() ? " [packed=true]" : "";
+ text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
+ << field.name() << " = " << field.number() << packed_comment << ';' << endl;
+
+ text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x";
+
+ ios::fmtflags fmt(text.flags());
+ text << setfill('0') << setw(16) << hex << get_field_id(field);
+ text.flags(fmt);
+
+ text << "L;" << endl;
+
+ text << endl;
+}
+
+/**
+ * Write a Message constants class.
+ */
+static void write_message(stringstream& text, const DescriptorProto& message,
+ const string& indent) {
+ int N;
+ const string indented = indent_more(indent);
+
+ text << indent << "// message " << message.name() << endl;
+ text << indent << "public final class " << message.name() << " {" << endl;
+ text << endl;
+
+ // Enums
+ N = message.enum_type_size();
+ for (int i = 0; i < N; i++) {
+ write_enum(text, message.enum_type(i), indented);
+ }
+
+ // Nested classes
+ N = message.nested_type_size();
+ for (int i = 0; i < N; i++) {
+ write_message(text, message.nested_type(i), indented);
+ }
+
+ // Fields
+ N = message.field_size();
+ for (int i = 0; i < N; i++) {
+ write_field(text, message.field(i), indented);
+ }
+
+ text << indent << "}" << endl;
+ text << endl;
+}
+
+/**
+ * Write the contents of a file.
+ *
+ * If there are enums and generate_outer is false, invalid java code will be generated.
+ */
+static void write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor,
+ const string& filename, bool generate_outer,
+ const vector<EnumDescriptorProto>& enums,
+ const vector<DescriptorProto>& messages) {
+ stringstream text;
+
+ string const package_name = make_java_package(file_descriptor);
+ string const outer_class_name = make_outer_class_name(file_descriptor);
+
+ text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl;
+ text << "// source: " << file_descriptor.name() << endl << endl;
+
+ if (package_name.size() > 0) {
+ if (package_name.size() > 0) {
+ text << "package " << package_name << ";" << endl;
+ text << endl;
+ }
+ }
+
+ // This bit of policy is android api rules specific: Raw proto classes
+ // must never be in the API
+ text << "/** @hide */" << endl;
+ // text << "@android.annotation.TestApi" << endl;
+
+ if (generate_outer) {
+ text << "public final class " << outer_class_name << " {" << endl;
+ text << endl;
+ }
+
+ size_t N;
+ const string indented = generate_outer ? indent_more("") : string();
+
+ N = enums.size();
+ for (size_t i = 0; i < N; i++) {
+ write_enum(text, enums[i], indented);
+ }
+
+ N = messages.size();
+ for (size_t i = 0; i < N; i++) {
+ write_message(text, messages[i], indented);
+ }
+
+ if (generate_outer) {
+ text << "}" << endl;
+ }
+
+ CodeGeneratorResponse::File* file_response = response->add_file();
+ file_response->set_name(filename);
+ file_response->set_content(text.str());
+}
+
+/**
+ * Write one file per class. Put all of the enums into the "outer" class.
+ */
+static void write_multiple_files(CodeGeneratorResponse* response,
+ const FileDescriptorProto& file_descriptor,
+ set<string> messages_to_compile) {
+ // If there is anything to put in the outer class file, create one
+ if (file_descriptor.enum_type_size() > 0) {
+ vector<EnumDescriptorProto> enums;
+ int N = file_descriptor.enum_type_size();
+ for (int i = 0; i < N; i++) {
+ auto enum_full_name =
+ file_descriptor.package() + "." + file_descriptor.enum_type(i).name();
+ if (!messages_to_compile.empty() && !messages_to_compile.count(enum_full_name)) {
+ continue;
+ }
+ enums.push_back(file_descriptor.enum_type(i));
+ }
+
+ vector<DescriptorProto> messages;
+
+ if (messages_to_compile.empty() || !enums.empty()) {
+ write_file(response, file_descriptor,
+ make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
+ true, enums, messages);
+ }
+ }
+
+ // For each of the message types, make a file
+ int N = file_descriptor.message_type_size();
+ for (int i = 0; i < N; i++) {
+ vector<EnumDescriptorProto> enums;
+
+ vector<DescriptorProto> messages;
+
+ auto message_full_name =
+ file_descriptor.package() + "." + file_descriptor.message_type(i).name();
+ if (!messages_to_compile.empty() && !messages_to_compile.count(message_full_name)) {
+ continue;
+ }
+ messages.push_back(file_descriptor.message_type(i));
+
+ if (messages_to_compile.empty() || !messages.empty()) {
+ write_file(response, file_descriptor,
+ make_file_name(file_descriptor, file_descriptor.message_type(i).name()),
+ false, enums, messages);
+ }
+ }
+}
+
+static void write_single_file(CodeGeneratorResponse* response,
+ const FileDescriptorProto& file_descriptor,
+ set<string> messages_to_compile) {
+ int N;
+
+ vector<EnumDescriptorProto> enums;
+ N = file_descriptor.enum_type_size();
+ for (int i = 0; i < N; i++) {
+ auto enum_full_name = file_descriptor.package() + "." + file_descriptor.enum_type(i).name();
+ if (!messages_to_compile.empty() && !messages_to_compile.count(enum_full_name)) {
+ continue;
+ }
+
+ enums.push_back(file_descriptor.enum_type(i));
+ }
+
+ vector<DescriptorProto> messages;
+ N = file_descriptor.message_type_size();
+ for (int i = 0; i < N; i++) {
+ auto message_full_name =
+ file_descriptor.package() + "." + file_descriptor.message_type(i).name();
+
+ if (!messages_to_compile.empty() && !messages_to_compile.count(message_full_name)) {
+ continue;
+ }
+
+ messages.push_back(file_descriptor.message_type(i));
+ }
+
+ if (messages_to_compile.empty() || !enums.empty() || !messages.empty()) {
+ write_file(response, file_descriptor,
+ make_file_name(file_descriptor, make_outer_class_name(file_descriptor)), true,
+ enums, messages);
+ }
+}
+
+static void parse_args_string(stringstream args_string_stream,
+ set<string>* messages_to_compile_out) {
+ string line;
+ while (getline(args_string_stream, line, ';')) {
+ stringstream line_ss(line);
+ string arg_name;
+ getline(line_ss, arg_name, ':');
+ if (arg_name == "include_filter") {
+ string full_message_name;
+ while (getline(line_ss, full_message_name, ',')) {
+ messages_to_compile_out->insert(full_message_name);
+ }
+ } else {
+ ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE, "Unexpected argument '%s'.", arg_name.c_str());
+ }
+ }
+}
+
+CodeGeneratorResponse generate_java_protostream_code(CodeGeneratorRequest request) {
+ CodeGeneratorResponse response;
+
+ set<string> messages_to_compile;
+ auto request_params = request.parameter();
+ if (!request_params.empty()) {
+ parse_args_string(stringstream(request_params), &messages_to_compile);
+ }
+
+ // Build the files we need.
+ const int N = request.proto_file_size();
+ for (int i = 0; i < N; i++) {
+ const FileDescriptorProto& file_descriptor = request.proto_file(i);
+ if (should_generate_for_file(request, file_descriptor.name())) {
+ if (file_descriptor.options().java_multiple_files()) {
+ write_multiple_files(&response, file_descriptor, messages_to_compile);
+ } else {
+ write_single_file(&response, file_descriptor, messages_to_compile);
+ }
+ }
+ }
+
+ return response;
+}
diff --git a/tools/streaming_proto/java/java_proto_stream_code_generator.h b/tools/streaming_proto/java/java_proto_stream_code_generator.h
new file mode 100644
index 000000000000..d2492f75d383
--- /dev/null
+++ b/tools/streaming_proto/java/java_proto_stream_code_generator.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef AOSP_MAIN_FRAMEWORKS_BASE_JAVAPROTOSTREAMCODEGENERATOR_H
+#define AOSP_MAIN_FRAMEWORKS_BASE_JAVAPROTOSTREAMCODEGENERATOR_H
+
+#include "stream_proto_utils.h"
+#include "string_utils.h"
+
+using namespace android::stream_proto;
+using namespace google::protobuf::io;
+using namespace std;
+
+CodeGeneratorResponse generate_java_protostream_code(CodeGeneratorRequest request);
+
+#endif // AOSP_MAIN_FRAMEWORKS_BASE_JAVAPROTOSTREAMCODEGENERATOR_H \ No newline at end of file
diff --git a/tools/streaming_proto/java/main.cpp b/tools/streaming_proto/java/main.cpp
index c9c50a561a04..5b35504865f8 100644
--- a/tools/streaming_proto/java/main.cpp
+++ b/tools/streaming_proto/java/main.cpp
@@ -1,268 +1,21 @@
-#include "Errors.h"
-#include "stream_proto_utils.h"
-#include "string_utils.h"
-
#include <stdio.h>
+
#include <iomanip>
#include <iostream>
-#include <sstream>
#include <map>
+#include <sstream>
+#include <string>
+
+#include "Errors.h"
+#include "java_proto_stream_code_generator.h"
+#include "stream_proto_utils.h"
using namespace android::stream_proto;
using namespace google::protobuf::io;
using namespace std;
/**
- * If the descriptor gives us a class name, use that. Otherwise make one up from
- * the filename of the .proto file.
- */
-static string
-make_outer_class_name(const FileDescriptorProto& file_descriptor)
-{
- string name = file_descriptor.options().java_outer_classname();
- if (name.size() == 0) {
- name = to_camel_case(file_base_name(file_descriptor.name()));
- if (name.size() == 0) {
- ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE,
- "Unable to make an outer class name for file: %s",
- file_descriptor.name().c_str());
- name = "Unknown";
- }
- }
- return name;
-}
-
-/**
- * Figure out the package name that we are generating.
- */
-static string
-make_java_package(const FileDescriptorProto& file_descriptor) {
- if (file_descriptor.options().has_java_package()) {
- return file_descriptor.options().java_package();
- } else {
- return file_descriptor.package();
- }
-}
-
-/**
- * Figure out the name of the file we are generating.
- */
-static string
-make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name)
-{
- string const package = make_java_package(file_descriptor);
- string result;
- if (package.size() > 0) {
- result = replace_string(package, '.', '/');
- result += '/';
- }
-
- result += class_name;
- result += ".java";
-
- return result;
-}
-
-static string
-indent_more(const string& indent)
-{
- return indent + INDENT;
-}
-
-/**
- * Write the constants for an enum.
- */
-static void
-write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
-{
- const int N = enu.value_size();
- text << indent << "// enum " << enu.name() << endl;
- for (int i=0; i<N; i++) {
- const EnumValueDescriptorProto& value = enu.value(i);
- text << indent << "public static final int "
- << make_constant_name(value.name())
- << " = " << value.number() << ";" << endl;
- }
- text << endl;
-}
-
-/**
- * Write a field.
- */
-static void
-write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
-{
- string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
- ? "optional " : "";
- string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
- ? "repeated " : "";
- string proto_type = get_proto_type(field);
- string packed_comment = field.options().packed()
- ? " [packed=true]" : "";
- text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
- << field.name() << " = " << field.number() << packed_comment << ';' << endl;
-
- text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x";
-
- ios::fmtflags fmt(text.flags());
- text << setfill('0') << setw(16) << hex << get_field_id(field);
- text.flags(fmt);
-
- text << "L;" << endl;
-
- text << endl;
-}
-
-/**
- * Write a Message constants class.
- */
-static void
-write_message(stringstream& text, const DescriptorProto& message, const string& indent)
-{
- int N;
- const string indented = indent_more(indent);
-
- text << indent << "// message " << message.name() << endl;
- text << indent << "public final class " << message.name() << " {" << endl;
- text << endl;
-
- // Enums
- N = message.enum_type_size();
- for (int i=0; i<N; i++) {
- write_enum(text, message.enum_type(i), indented);
- }
-
- // Nested classes
- N = message.nested_type_size();
- for (int i=0; i<N; i++) {
- write_message(text, message.nested_type(i), indented);
- }
-
- // Fields
- N = message.field_size();
- for (int i=0; i<N; i++) {
- write_field(text, message.field(i), indented);
- }
-
- text << indent << "}" << endl;
- text << endl;
-}
-
-/**
- * Write the contents of a file.
*
- * If there are enums and generate_outer is false, invalid java code will be generated.
- */
-static void
-write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor,
- const string& filename, bool generate_outer,
- const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages)
-{
- stringstream text;
-
- string const package_name = make_java_package(file_descriptor);
- string const outer_class_name = make_outer_class_name(file_descriptor);
-
- text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl;
- text << "// source: " << file_descriptor.name() << endl << endl;
-
- if (package_name.size() > 0) {
- if (package_name.size() > 0) {
- text << "package " << package_name << ";" << endl;
- text << endl;
- }
- }
-
- // This bit of policy is android api rules specific: Raw proto classes
- // must never be in the API
- text << "/** @hide */" << endl;
-// text << "@android.annotation.TestApi" << endl;
-
- if (generate_outer) {
- text << "public final class " << outer_class_name << " {" << endl;
- text << endl;
- }
-
- size_t N;
- const string indented = generate_outer ? indent_more("") : string();
-
- N = enums.size();
- for (size_t i=0; i<N; i++) {
- write_enum(text, enums[i], indented);
- }
-
- N = messages.size();
- for (size_t i=0; i<N; i++) {
- write_message(text, messages[i], indented);
- }
-
- if (generate_outer) {
- text << "}" << endl;
- }
-
- CodeGeneratorResponse::File* file_response = response->add_file();
- file_response->set_name(filename);
- file_response->set_content(text.str());
-}
-
-/**
- * Write one file per class. Put all of the enums into the "outer" class.
- */
-static void
-write_multiple_files(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
-{
- // If there is anything to put in the outer class file, create one
- if (file_descriptor.enum_type_size() > 0) {
- vector<EnumDescriptorProto> enums;
- int N = file_descriptor.enum_type_size();
- for (int i=0; i<N; i++) {
- enums.push_back(file_descriptor.enum_type(i));
- }
-
- vector<DescriptorProto> messages;
-
- write_file(response, file_descriptor,
- make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
- true, enums, messages);
- }
-
- // For each of the message types, make a file
- int N = file_descriptor.message_type_size();
- for (int i=0; i<N; i++) {
- vector<EnumDescriptorProto> enums;
-
- vector<DescriptorProto> messages;
- messages.push_back(file_descriptor.message_type(i));
-
- write_file(response, file_descriptor,
- make_file_name(file_descriptor, file_descriptor.message_type(i).name()),
- false, enums, messages);
- }
-}
-
-static void
-write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
-{
- int N;
-
- vector<EnumDescriptorProto> enums;
- N = file_descriptor.enum_type_size();
- for (int i=0; i<N; i++) {
- enums.push_back(file_descriptor.enum_type(i));
- }
-
- vector<DescriptorProto> messages;
- N = file_descriptor.message_type_size();
- for (int i=0; i<N; i++) {
- messages.push_back(file_descriptor.message_type(i));
- }
-
- write_file(response, file_descriptor,
- make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
- true, enums, messages);
-}
-
-/**
* Main.
*/
int
@@ -273,24 +26,11 @@ main(int argc, char const*const* argv)
GOOGLE_PROTOBUF_VERIFY_VERSION;
- CodeGeneratorRequest request;
- CodeGeneratorResponse response;
-
// Read the request
+ CodeGeneratorRequest request;
request.ParseFromIstream(&cin);
- // Build the files we need.
- const int N = request.proto_file_size();
- for (int i=0; i<N; i++) {
- const FileDescriptorProto& file_descriptor = request.proto_file(i);
- if (should_generate_for_file(request, file_descriptor.name())) {
- if (file_descriptor.options().java_multiple_files()) {
- write_multiple_files(&response, file_descriptor);
- } else {
- write_single_file(&response, file_descriptor);
- }
- }
- }
+ CodeGeneratorResponse response = generate_java_protostream_code(request);
// If we had errors, don't write the response. Print the errors and exit.
if (ERRORS.HasErrors()) {
diff --git a/tools/streaming_proto/test/imported.proto b/tools/streaming_proto/test/integration/imported.proto
index 05c8f0c54fac..05c8f0c54fac 100644
--- a/tools/streaming_proto/test/imported.proto
+++ b/tools/streaming_proto/test/integration/imported.proto
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/captioning/CaptioningModule.kt b/tools/streaming_proto/test/integration/src/com/android/streaming_proto_test/Main.java
index aeb5c5d72be6..2a7001b294ea 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/captioning/CaptioningModule.kt
+++ b/tools/streaming_proto/test/integration/src/com/android/streaming_proto_test/Main.java
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.volume.panel.component.captioning
+package com.android.streaming_proto_test;
-import dagger.Module
-
-@Module interface CaptioningModule
+public class Main {
+ public void main(String[] argv) {
+ System.out.println("hello world");
+ }
+}
diff --git a/tools/streaming_proto/test/test.proto b/tools/streaming_proto/test/integration/test.proto
index de80ed6612fc..3cf81b4ffd56 100644
--- a/tools/streaming_proto/test/test.proto
+++ b/tools/streaming_proto/test/integration/test.proto
@@ -16,7 +16,7 @@
syntax = "proto2";
-import "frameworks/base/tools/streaming_proto/test/imported.proto";
+import "frameworks/base/tools/streaming_proto/test/integration/imported.proto";
package com.android.streaming_proto_test;
diff --git a/tools/streaming_proto/test/src/com/android/streaming_proto_test/Main.java b/tools/streaming_proto/test/src/com/android/streaming_proto_test/Main.java
deleted file mode 100644
index 1246f539b44b..000000000000
--- a/tools/streaming_proto/test/src/com/android/streaming_proto_test/Main.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.streaming_proto_test;
-
-public class Main {
- public void main(String[] argv) {
- System.out.println("hello world");
- }
-}
diff --git a/tools/streaming_proto/test/unit/streaming_proto_java.cpp b/tools/streaming_proto/test/unit/streaming_proto_java.cpp
new file mode 100644
index 000000000000..8df9716b312d
--- /dev/null
+++ b/tools/streaming_proto/test/unit/streaming_proto_java.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "java/java_proto_stream_code_generator.h"
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+static void add_my_test_proto_file(CodeGeneratorRequest* request) {
+ request->add_file_to_generate("MyTestProtoFile");
+
+ FileDescriptorProto* file_desc = request->add_proto_file();
+ file_desc->set_name("MyTestProtoFile");
+ file_desc->set_package("test.package");
+
+ auto* file_options = file_desc->mutable_options();
+ file_options->set_java_multiple_files(false);
+
+ auto* message = file_desc->add_message_type();
+ message->set_name("MyTestMessage");
+
+ auto* field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("my_test_field");
+
+ field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("my_other_test_field");
+
+ field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("my_other_test_message");
+}
+
+static void add_my_other_test_proto_file(CodeGeneratorRequest* request) {
+ request->add_file_to_generate("MyOtherTestProtoFile");
+
+ FileDescriptorProto* file_desc = request->add_proto_file();
+ file_desc->set_name("MyOtherTestProtoFile");
+ file_desc->set_package("test.package");
+
+ auto* file_options = file_desc->mutable_options();
+ file_options->set_java_multiple_files(false);
+
+ auto* message = file_desc->add_message_type();
+ message->set_name("MyOtherTestMessage");
+
+ auto* field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("a_test_field");
+
+ field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("another_test_field");
+}
+
+static CodeGeneratorRequest create_simple_two_file_request() {
+ CodeGeneratorRequest request;
+
+ add_my_test_proto_file(&request);
+ add_my_other_test_proto_file(&request);
+
+ return request;
+}
+
+static CodeGeneratorRequest create_simple_multi_file_request() {
+ CodeGeneratorRequest request;
+
+ request.add_file_to_generate("MyMultiMessageTestProtoFile");
+
+ FileDescriptorProto* file_desc = request.add_proto_file();
+ file_desc->set_name("MyMultiMessageTestProtoFile");
+ file_desc->set_package("test.package");
+
+ auto* file_options = file_desc->mutable_options();
+ file_options->set_java_multiple_files(true);
+
+ auto* message = file_desc->add_message_type();
+ message->set_name("MyTestMessage");
+
+ auto* field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("my_test_field");
+
+ field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("my_other_test_field");
+
+ field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("my_other_test_message");
+
+ message = file_desc->add_message_type();
+ message->set_name("MyOtherTestMessage");
+
+ field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("a_test_field");
+
+ field = message->add_field();
+ field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
+ field->set_name("another_test_field");
+
+ return request;
+}
+
+TEST(StreamingProtoJavaTest, NoFilter) {
+ CodeGeneratorRequest request = create_simple_two_file_request();
+ CodeGeneratorResponse response = generate_java_protostream_code(request);
+
+ auto generated_file_count = response.file_size();
+ EXPECT_EQ(generated_file_count, 2);
+
+ EXPECT_EQ(response.file(0).name(), "test/package/MyTestProtoFile.java");
+ EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestProtoFile"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD"));
+
+ EXPECT_EQ(response.file(1).name(), "test/package/MyOtherTestProtoFile.java");
+ EXPECT_THAT(response.file(1).content(), HasSubstr("class MyOtherTestProtoFile"));
+ EXPECT_THAT(response.file(1).content(), HasSubstr("class MyOtherTestMessage"));
+ EXPECT_THAT(response.file(1).content(), HasSubstr("long A_TEST_FIELD"));
+ EXPECT_THAT(response.file(1).content(), HasSubstr("long ANOTHER_TEST_FIELD"));
+}
+
+TEST(StreamingProtoJavaTest, WithFilter) {
+ CodeGeneratorRequest request = create_simple_two_file_request();
+ request.set_parameter("include_filter:test.package.MyTestMessage");
+ CodeGeneratorResponse response = generate_java_protostream_code(request);
+
+ auto generated_file_count = response.file_size();
+ EXPECT_EQ(generated_file_count, 1);
+
+ EXPECT_EQ(response.file(0).name(), "test/package/MyTestProtoFile.java");
+ EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestProtoFile"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD"));
+}
+
+TEST(StreamingProtoJavaTest, WithoutFilter_MultipleJavaFiles) {
+ CodeGeneratorRequest request = create_simple_multi_file_request();
+ CodeGeneratorResponse response = generate_java_protostream_code(request);
+
+ auto generated_file_count = response.file_size();
+ EXPECT_EQ(generated_file_count, 2);
+
+ EXPECT_EQ(response.file(0).name(), "test/package/MyTestMessage.java");
+ EXPECT_THAT(response.file(0).content(), Not(HasSubstr("class MyTestProtoFile")));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD"));
+
+ EXPECT_EQ(response.file(1).name(), "test/package/MyOtherTestMessage.java");
+ EXPECT_THAT(response.file(1).content(), Not(HasSubstr("class MyOtherTestProtoFile")));
+ EXPECT_THAT(response.file(1).content(), HasSubstr("class MyOtherTestMessage"));
+ EXPECT_THAT(response.file(1).content(), HasSubstr("long A_TEST_FIELD"));
+ EXPECT_THAT(response.file(1).content(), HasSubstr("long ANOTHER_TEST_FIELD"));
+}
+
+TEST(StreamingProtoJavaTest, WithFilter_MultipleJavaFiles) {
+ CodeGeneratorRequest request = create_simple_multi_file_request();
+ request.set_parameter("include_filter:test.package.MyTestMessage");
+ CodeGeneratorResponse response = generate_java_protostream_code(request);
+
+ auto generated_file_count = response.file_size();
+ EXPECT_EQ(generated_file_count, 1);
+
+ EXPECT_EQ(response.file(0).name(), "test/package/MyTestMessage.java");
+ EXPECT_THAT(response.file(0).content(), Not(HasSubstr("class MyTestProtoFile")));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD"));
+ EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD"));
+}
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 58638e8e1af4..45ab9863ff73 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -718,6 +718,9 @@ public class WifiNl80211Manager {
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IClientInterface due to remote exception");
return false;
+ } catch (NullPointerException e2) {
+ Log.e(TAG, "setupInterfaceForClientMode NullPointerException");
+ return false;
}
if (clientInterface == null) {
@@ -785,6 +788,9 @@ public class WifiNl80211Manager {
} catch (RemoteException e1) {
Log.e(TAG, "Failed to teardown client interface due to remote exception");
return false;
+ } catch (NullPointerException e2) {
+ Log.e(TAG, "tearDownClientInterface NullPointerException");
+ return false;
}
if (!success) {
Log.e(TAG, "Failed to teardown client interface");
@@ -816,6 +822,9 @@ public class WifiNl80211Manager {
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IApInterface due to remote exception");
return false;
+ } catch (NullPointerException e2) {
+ Log.e(TAG, "setupInterfaceForSoftApMode NullPointerException");
+ return false;
}
if (apInterface == null) {
@@ -854,6 +863,9 @@ public class WifiNl80211Manager {
} catch (RemoteException e1) {
Log.e(TAG, "Failed to teardown AP interface due to remote exception");
return false;
+ } catch (NullPointerException e2) {
+ Log.e(TAG, "tearDownSoftApInterface NullPointerException");
+ return false;
}
if (!success) {
Log.e(TAG, "Failed to teardown AP interface");
@@ -1328,6 +1340,8 @@ public class WifiNl80211Manager {
}
} catch (RemoteException e1) {
Log.e(TAG, "Failed to request getChannelsForBand due to remote exception");
+ } catch (NullPointerException e2) {
+ Log.e(TAG, "getChannelsMhzForBand NullPointerException");
}
if (result == null) {
result = new int[0];
@@ -1352,7 +1366,8 @@ public class WifiNl80211Manager {
*/
@Nullable public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) {
if (mWificond == null) {
- Log.e(TAG, "getDeviceWiphyCapabilities: mWificond binder is null! Did wificond die?");
+ Log.e(TAG, "getDeviceWiphyCapabilities: mWificond binder is null! "
+ + "Did wificond die?");
return null;
}
@@ -1360,6 +1375,9 @@ public class WifiNl80211Manager {
return mWificond.getDeviceWiphyCapabilities(ifaceName);
} catch (RemoteException e) {
return null;
+ } catch (NullPointerException e2) {
+ Log.e(TAG, "getDeviceWiphyCapabilities NullPointerException");
+ return null;
}
}
@@ -1409,6 +1427,8 @@ public class WifiNl80211Manager {
Log.i(TAG, "Receive country code change to " + newCountryCode);
} catch (RemoteException re) {
re.rethrowFromSystemServer();
+ } catch (NullPointerException e) {
+ new RemoteException("Wificond service doesn't exist!").rethrowFromSystemServer();
}
}