diff options
72 files changed, 1472 insertions, 1495 deletions
diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h index 65916d7ebf49..2fc4d43b798a 100644 --- a/cmds/idmap2/include/idmap2/FabricatedOverlay.h +++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h @@ -66,18 +66,21 @@ struct FabricatedOverlay { private: struct SerializedData { - std::unique_ptr<uint8_t[]> data; - size_t data_size; - uint32_t crc; - }; + std::unique_ptr<uint8_t[]> pb_data; + size_t pb_data_size; + uint32_t pb_crc; + std::string sp_data; + }; Result<SerializedData*> InitializeData() const; Result<uint32_t> GetCrc() const; explicit FabricatedOverlay(pb::FabricatedOverlay&& overlay, + std::string&& string_pool_data_, std::optional<uint32_t> crc_from_disk = {}); pb::FabricatedOverlay overlay_pb_; + std::string string_pool_data_; std::optional<uint32_t> crc_from_disk_; mutable std::optional<SerializedData> data_; diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp index 4d49674efce3..5bbe08524c18 100644 --- a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp +++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp @@ -17,8 +17,9 @@ #include "idmap2/FabricatedOverlay.h" #include <androidfw/ResourceUtils.h> +#include <androidfw/StringPool.h> #include <google/protobuf/io/coded_stream.h> -#include <google/protobuf/io/zero_copy_stream_impl_lite.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> #include <utils/ByteOrder.h> #include <zlib.h> @@ -30,6 +31,8 @@ namespace android::idmap2 { +constexpr auto kBufferSize = 1024; + namespace { bool Read32(std::istream& stream, uint32_t* out) { uint32_t value; @@ -47,8 +50,11 @@ void Write32(std::ostream& stream, uint32_t value) { } // namespace FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay, + std::string&& string_pool_data, std::optional<uint32_t> crc_from_disk) - : overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), crc_from_disk_(crc_from_disk) { + : overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), + string_pool_data_(std::move(string_pool_data)), + crc_from_disk_(crc_from_disk) { } FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name, @@ -76,7 +82,11 @@ FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue( } Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { - std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> entries; + using EntryMap = std::map<std::string, TargetValue>; + using TypeMap = std::map<std::string, EntryMap>; + using PackageMap = std::map<std::string, TypeMap>; + PackageMap package_map; + android::StringPool string_pool; for (const auto& res_entry : entries_) { StringPiece package_substr; StringPiece type_name; @@ -96,11 +106,10 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { return Error("resource name '%s' missing entry name", res_entry.resource_name.c_str()); } - auto package = entries.find(package_name); - if (package == entries.end()) { - package = entries - .insert(std::make_pair( - package_name, std::map<std::string, std::map<std::string, TargetValue>>())) + auto package = package_map.find(package_name); + if (package == package_map.end()) { + package = package_map + .insert(std::make_pair(package_name, TypeMap())) .first; } @@ -108,7 +117,7 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { if (type == package->second.end()) { type = package->second - .insert(std::make_pair(type_name.to_string(), std::map<std::string, TargetValue>())) + .insert(std::make_pair(type_name.to_string(), EntryMap())) .first; } @@ -127,25 +136,32 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { overlay_pb.set_target_package_name(target_package_name_); overlay_pb.set_target_overlayable(target_overlayable_); - for (const auto& package : entries) { + for (auto& package : package_map) { auto package_pb = overlay_pb.add_packages(); package_pb->set_name(package.first); - for (const auto& type : package.second) { + for (auto& type : package.second) { auto type_pb = package_pb->add_types(); type_pb->set_name(type.first); - for (const auto& entry : type.second) { + for (auto& entry : type.second) { auto entry_pb = type_pb->add_entries(); entry_pb->set_name(entry.first); pb::ResourceValue* value = entry_pb->mutable_res_value(); value->set_data_type(entry.second.data_type); - value->set_data_value(entry.second.data_value); + if (entry.second.data_type == Res_value::TYPE_STRING) { + auto ref = string_pool.MakeRef(entry.second.data_string_value); + value->set_data_value(ref.index()); + } else { + value->set_data_value(entry.second.data_value); + } } } } - return FabricatedOverlay(std::move(overlay_pb)); + android::BigBuffer string_buffer(kBufferSize); + android::StringPool::FlattenUtf8(&string_buffer, string_pool, nullptr); + return FabricatedOverlay(std::move(overlay_pb), string_buffer.to_string()); } Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) { @@ -163,48 +179,67 @@ Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stre return Error("Failed to read fabricated overlay version."); } - if (version != 1) { + if (version != 1 && version != 2) { return Error("Invalid fabricated overlay version '%u'.", version); } uint32_t crc; if (!Read32(stream, &crc)) { - return Error("Failed to read fabricated overlay version."); + return Error("Failed to read fabricated overlay crc."); } pb::FabricatedOverlay overlay{}; - if (!overlay.ParseFromIstream(&stream)) { - return Error("Failed read fabricated overlay proto."); + std::string sp_data; + if (version == 2) { + uint32_t sp_size; + if (!Read32(stream, &sp_size)) { + return Error("Failed read string pool size."); + } + std::string buf(sp_size, '\0'); + if (!stream.read(buf.data(), sp_size)) { + return Error("Failed to read string pool."); + } + sp_data = buf; + + if (!overlay.ParseFromIstream(&stream)) { + return Error("Failed read fabricated overlay proto."); + } + } else { + if (!overlay.ParseFromIstream(&stream)) { + return Error("Failed read fabricated overlay proto."); + } } // If the proto version is the latest version, then the contents of the proto must be the same // when the proto is re-serialized; otherwise, the crc must be calculated because migrating the // proto to the latest version will likely change the contents of the fabricated overlay. - return FabricatedOverlay(std::move(overlay), version == kFabricatedOverlayCurrentVersion + return FabricatedOverlay(std::move(overlay), std::move(sp_data), + version == kFabricatedOverlayCurrentVersion ? std::optional<uint32_t>(crc) : std::nullopt); } Result<FabricatedOverlay::SerializedData*> FabricatedOverlay::InitializeData() const { if (!data_.has_value()) { - auto size = overlay_pb_.ByteSizeLong(); - auto data = std::unique_ptr<uint8_t[]>(new uint8_t[size]); + auto pb_size = overlay_pb_.ByteSizeLong(); + auto pb_data = std::unique_ptr<uint8_t[]>(new uint8_t[pb_size]); // Ensure serialization is deterministic - google::protobuf::io::ArrayOutputStream array_stream(data.get(), size); + google::protobuf::io::ArrayOutputStream array_stream(pb_data.get(), pb_size); google::protobuf::io::CodedOutputStream output_stream(&array_stream); output_stream.SetSerializationDeterministic(true); overlay_pb_.SerializeWithCachedSizes(&output_stream); - if (output_stream.HadError() || size != output_stream.ByteCount()) { + if (output_stream.HadError() || pb_size != output_stream.ByteCount()) { return Error("Failed to serialize fabricated overlay."); } // Calculate the crc using the proto data and the version. - uint32_t crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, reinterpret_cast<const uint8_t*>(&kFabricatedOverlayCurrentVersion), + uint32_t pb_crc = crc32(0L, Z_NULL, 0); + pb_crc = crc32(pb_crc, reinterpret_cast<const uint8_t*>(&kFabricatedOverlayCurrentVersion), sizeof(uint32_t)); - crc = crc32(crc, data.get(), size); - data_ = SerializedData{std::move(data), size, crc}; + pb_crc = crc32(pb_crc, pb_data.get(), pb_size); + + data_ = SerializedData{std::move(pb_data), pb_size, pb_crc, string_pool_data_}; } return &(*data_); } @@ -216,7 +251,7 @@ Result<uint32_t> FabricatedOverlay::GetCrc() const { if (!data) { return data.GetError(); } - return (*data)->crc; + return (*data)->pb_crc; } Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const { @@ -227,8 +262,13 @@ Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const { Write32(stream, kFabricatedOverlayMagic); Write32(stream, kFabricatedOverlayCurrentVersion); - Write32(stream, (*data)->crc); - stream.write(reinterpret_cast<const char*>((*data)->data.get()), (*data)->data_size); + Write32(stream, (*data)->pb_crc); + Write32(stream, (*data)->sp_data.length()); + stream.write((*data)->sp_data.data(), (*data)->sp_data.length()); + if (stream.bad()) { + return Error("Failed to write string pool data."); + } + stream.write(reinterpret_cast<const char*>((*data)->pb_data.get()), (*data)->pb_data_size); if (stream.bad()) { return Error("Failed to write serialized fabricated overlay."); } @@ -295,6 +335,14 @@ Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info } } } + const uint32_t string_pool_data_length = overlay_.string_pool_data_.length(); + result.string_pool_data = OverlayData::InlineStringPoolData{ + .data = std::unique_ptr<uint8_t[]>(new uint8_t[string_pool_data_length]), + .data_length = string_pool_data_length, + .string_pool_offset = 0, + }; + memcpy(result.string_pool_data->data.get(), overlay_.string_pool_data_.data(), + string_pool_data_length); return result; } diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp index 468ea0c634c1..91331ca1cc76 100644 --- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp +++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp @@ -46,6 +46,7 @@ TEST(FabricatedOverlayTests, SetResourceValue) { .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U) .SetResourceValue("com.example.target.split:integer/int2", Res_value::TYPE_INT_DEC, 2U) .SetResourceValue("string/int3", Res_value::TYPE_REFERENCE, 0x7f010000) + .SetResourceValue("com.example.target:string/string1", Res_value::TYPE_STRING, "foobar") .Build(); ASSERT_TRUE(overlay); auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay)); @@ -59,8 +60,9 @@ TEST(FabricatedOverlayTests, SetResourceValue) { auto pairs = container->GetOverlayData(*info); ASSERT_TRUE(pairs); - EXPECT_FALSE(pairs->string_pool_data.has_value()); - ASSERT_EQ(3U, pairs->pairs.size()); + ASSERT_EQ(4U, pairs->pairs.size()); + auto string_pool = ResStringPool(pairs->string_pool_data->data.get(), + pairs->string_pool_data->data_length, false); auto& it = pairs->pairs[0]; ASSERT_EQ("com.example.target:integer/int1", it.resource_name); @@ -77,6 +79,13 @@ TEST(FabricatedOverlayTests, SetResourceValue) { ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->data_type); it = pairs->pairs[2]; + ASSERT_EQ("com.example.target:string/string1", it.resource_name); + entry = std::get_if<TargetValue>(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(Res_value::TYPE_STRING, entry->data_type); + ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->data_value).value_or("")); + + it = pairs->pairs[3]; ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name); entry = std::get_if<TargetValue>(&it.value); ASSERT_NE(nullptr, entry); @@ -104,6 +113,7 @@ TEST(FabricatedOverlayTests, SerializeAndDeserialize) { FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") .SetOverlayable("TestResources") .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U) + .SetResourceValue("com.example.target:string/string1", Res_value::TYPE_STRING, "foobar") .Build(); ASSERT_TRUE(overlay); TemporaryFile tf; @@ -126,7 +136,9 @@ TEST(FabricatedOverlayTests, SerializeAndDeserialize) { auto pairs = (*container)->GetOverlayData(*info); ASSERT_TRUE(pairs) << pairs.GetErrorMessage(); - EXPECT_EQ(1U, pairs->pairs.size()); + EXPECT_EQ(2U, pairs->pairs.size()); + auto string_pool = ResStringPool(pairs->string_pool_data->data.get(), + pairs->string_pool_data->data_length, false); auto& it = pairs->pairs[0]; ASSERT_EQ("com.example.target:integer/int1", it.resource_name); @@ -134,6 +146,13 @@ TEST(FabricatedOverlayTests, SerializeAndDeserialize) { ASSERT_NE(nullptr, entry); EXPECT_EQ(1U, entry->data_value); EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->data_type); + + it = pairs->pairs[1]; + ASSERT_EQ("com.example.target:string/string1", it.resource_name); + entry = std::get_if<TargetValue>(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(Res_value::TYPE_STRING, entry->data_type); + ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->data_value).value_or("")); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 738b9cf237c9..a3799f98c90c 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -263,6 +263,7 @@ TEST(IdmapTests, FabricatedOverlay) { .SetOverlayable("TestResources") .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U) .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000) + .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar") .Build(); ASSERT_TRUE(frro); @@ -288,12 +289,19 @@ TEST(IdmapTests, FabricatedOverlay) { ASSERT_EQ(data->GetTargetEntries().size(), 0U); ASSERT_EQ(data->GetOverlayEntries().size(), 0U); + auto string_pool_data = data->GetStringPoolData(); + auto string_pool = ResStringPool(string_pool_data.data(), string_pool_data.size(), false); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); - ASSERT_EQ(target_inline_entries.size(), 2U); + ASSERT_EQ(target_inline_entries.size(), 3U); ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U); ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000); + ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2, + Res_value::TYPE_STRING, + (uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1)); } TEST(IdmapTests, FailCreateIdmapInvalidName) { diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h index 89219c9c8213..ad998b9a7a4c 100644 --- a/cmds/idmap2/tests/R.h +++ b/cmds/idmap2/tests/R.h @@ -41,6 +41,7 @@ namespace R::target { constexpr ResourceId policy_system = 0x7f02000c; constexpr ResourceId policy_system_vendor = 0x7f02000d; constexpr ResourceId str1 = 0x7f02000e; + constexpr ResourceId str2 = 0x7f02000f; constexpr ResourceId str3 = 0x7f020010; constexpr ResourceId str4 = 0x7f020011; } // namespace string diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 32b3d1326d92..c05abcf31532 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -196,6 +196,7 @@ TEST(ResourceMappingTests, FabricatedOverlay) { .SetOverlayable("TestResources") .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U) .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000) + .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar") .Build(); ASSERT_TRUE(frro); @@ -209,9 +210,14 @@ TEST(ResourceMappingTests, FabricatedOverlay) { ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; - ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U); + auto string_pool_data = res.GetStringPoolData(); + auto string_pool = ResStringPool(string_pool_data.data(), string_pool_data.size(), false); + + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U); ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000)); + ASSERT_RESULT(MappingExists(res, R::target::string::str2, Res_value::TYPE_STRING, + (uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1))); ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U)); } diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java index b1b432bf79ab..6fd2bf250e2c 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java @@ -380,7 +380,7 @@ public class UiDevice { Tracer.trace(); Display display = getAutomatorBridge().getDefaultDisplay(); Point p = new Point(); - display.getSize(p); + display.getRealSize(p); return p.x; } @@ -394,7 +394,7 @@ public class UiDevice { Tracer.trace(); Display display = getAutomatorBridge().getDefaultDisplay(); Point p = new Point(); - display.getSize(p); + display.getRealSize(p); return p.y; } diff --git a/core/api/current.txt b/core/api/current.txt index 1eb4c02ed1c3..32bdec010647 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -41342,6 +41342,8 @@ package android.telephony { field public static final String KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING = "carrier_instant_lettering_escaped_chars_string"; field public static final String KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING = "carrier_instant_lettering_invalid_chars_string"; field public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = "carrier_instant_lettering_length_limit_int"; + field public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS = "carrier_metered_apn_types_strings"; + field public static final String KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS = "carrier_metered_roaming_apn_types_strings"; field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool"; field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string"; field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array"; diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 7247ef77afb4..7092e43596ec 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -29,7 +29,6 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; -import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.CryptoObject; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; @@ -675,45 +674,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Forwards BiometricStateListener to FaceService. - * - * @param listener new BiometricStateListener being added - * @hide - */ - public void registerBiometricStateListener(@NonNull BiometricStateListener listener) { - try { - mService.registerBiometricStateListener(listener); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Adds a callback that gets called when the service registers all of the face - * authenticators (HALs). - * - * If the face authenticators are already registered when the callback is added, the - * callback is invoked immediately. - * - * The callback is automatically removed after it's invoked. - * - * @hide - */ - @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void addAuthenticatorsRegisteredCallback( - IFaceAuthenticatorsRegisteredCallback callback) { - if (mService != null) { - try { - mService.addAuthenticatorsRegisteredCallback(callback); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } else { - Slog.w(TAG, "addAuthenticatorsRegisteredCallback(): Service not connected!"); - } - } - - /** * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) diff --git a/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl deleted file mode 100644 index 78f978d21ed7..000000000000 --- a/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl +++ /dev/null @@ -1,35 +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 android.hardware.face; - -import android.hardware.face.FaceSensorPropertiesInternal; -import java.util.List; - -/** - * Callback to notify FaceManager that FaceService has registered all of the - * face authenticators (HALs). - * See {@link android.hardware.face.IFaceService#registerAuthenticators}. - * - * @hide - */ -oneway interface IFaceAuthenticatorsRegisteredCallback { - /** - * Notifies FaceManager that all of the face authenticators have been registered. - * - * @param sensors A consolidated list of sensor properties for all of the authenticators. - */ - void onAllAuthenticatorsRegistered(in List<FaceSensorPropertiesInternal> sensors); -} diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 9b56f43a0f22..369248edd580 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -17,11 +17,9 @@ package android.hardware.face; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; -import android.hardware.biometrics.IBiometricStateListener; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.Face; import android.hardware.face.FaceSensorPropertiesInternal; @@ -165,11 +163,4 @@ interface IFaceService { // hidlSensors must be non-null and empty. See AuthService.java @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors); - - // Adds a callback which gets called when the service registers all of the face - // authenticators. The callback is automatically removed after it's invoked. - void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback); - - // Registers BiometricStateListener. - void registerBiometricStateListener(IBiometricStateListener listener); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 1ba9a0471c88..cc7ed183ed64 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -202,10 +202,8 @@ interface IFingerprintService { void setSidefpsController(in ISidefpsController controller); // Registers BiometricStateListener. - @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerBiometricStateListener(IBiometricStateListener listener); // Sends a power button pressed event to all listeners. - @EnforcePermission("USE_BIOMETRIC_INTERNAL") oneway void onPowerPressed(); } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index 3e0deeb556e9..53ae6576ef95 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -35,6 +35,8 @@ interface IDreamManager { void testDream(int userId, in ComponentName componentName); @UnsupportedAppUsage boolean isDreaming(); + @UnsupportedAppUsage + boolean isDreamingOrInPreview(); void finishSelf(in IBinder token, boolean immediate); void startDozing(in IBinder token, int screenState, int screenBrightness); void stopDozing(in IBinder token); diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto index c465233036c4..a96ec41d3708 100644 --- a/core/proto/android/server/peopleservice.proto +++ b/core/proto/android/server/peopleservice.proto @@ -62,7 +62,10 @@ message ConversationInfoProto { // The timestamp of the last event in millis. optional int64 last_event_timestamp = 9; - // Next tag: 10 + // The timestamp this conversation was created in millis. + optional int64 creation_timestamp = 10; + + // Next tag: 11 } // On disk data of events. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9faf5e85c31f..9890614c010c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2000,6 +2000,18 @@ are controlled together (aliasing is true) or not. --> <bool name="config_alias_ring_notif_stream_types">true</bool> + <!-- The number of volume steps for the notification stream --> + <integer name="config_audio_notif_vol_steps">7</integer> + + <!-- The default volume for the notification stream --> + <integer name="config_audio_notif_vol_default">5</integer> + + <!-- The number of volume steps for the ring stream --> + <integer name="config_audio_ring_vol_steps">7</integer> + + <!-- The default volume for the ring stream --> + <integer name="config_audio_ring_vol_default">5</integer> + <!-- Flag indicating whether platform level volume adjustments are enabled for remote sessions on grouped devices. --> <bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 51712ff5e35f..00dab0416af1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -276,6 +276,10 @@ <java-symbol type="bool" name="action_bar_embed_tabs" /> <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" /> <java-symbol type="bool" name="config_alias_ring_notif_stream_types" /> + <java-symbol type="integer" name="config_audio_notif_vol_default" /> + <java-symbol type="integer" name="config_audio_notif_vol_steps" /> + <java-symbol type="integer" name="config_audio_ring_vol_default" /> + <java-symbol type="integer" name="config_audio_ring_vol_steps" /> <java-symbol type="bool" name="config_avoidGfxAccel" /> <java-symbol type="bool" name="config_bluetooth_address_validation" /> <java-symbol type="integer" name="config_chooser_max_targets_per_row" /> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index a0e22011b5d0..7619646804ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -288,8 +288,10 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen if (mTargetViewContainer.getVisibility() != View.VISIBLE) { mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this); - mTargetViewContainer.show(); } + // always invoke show, since the target might still be VISIBLE while playing hide animation, + // so we want to ensure it will show back again + mTargetViewContainer.show(); } /** Animates the magnetic dismiss target out and then sets it to GONE. */ diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 2beb33abe782..9aa37872b8de 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -141,6 +141,9 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_ return {}; } loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags); + } else if (loaded_idmap != nullptr && + IsFabricatedOverlay(std::string(loaded_idmap->OverlayApkPath()))) { + loaded_arsc = LoadedArsc::Load(loaded_idmap.get()); } else { loaded_arsc = LoadedArsc::CreateEmpty(); } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 35b6170fae5b..5b69cca2d747 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -820,6 +820,13 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return true; } +bool LoadedArsc::LoadStringPool(const LoadedIdmap* loaded_idmap) { + if (loaded_idmap != nullptr) { + global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap); + } + return true; +} + std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data, const size_t length, const LoadedIdmap* loaded_idmap, @@ -855,6 +862,16 @@ std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data, return loaded_arsc; } +std::unique_ptr<LoadedArsc> LoadedArsc::Load(const LoadedIdmap* loaded_idmap) { + ATRACE_NAME("LoadedArsc::Load"); + + // Not using make_unique because the constructor is private. + std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); + loaded_arsc->LoadStringPool(loaded_idmap); + return loaded_arsc; +} + + std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() { return std::unique_ptr<LoadedArsc>(new LoadedArsc()); } diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index b3d6a4dcb955..e45963950b04 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -314,6 +314,8 @@ class LoadedArsc { const LoadedIdmap* loaded_idmap = nullptr, package_property_t property_flags = 0U); + static std::unique_ptr<LoadedArsc> Load(const LoadedIdmap* loaded_idmap = nullptr); + // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. static std::unique_ptr<LoadedArsc> CreateEmpty(); @@ -338,6 +340,7 @@ class LoadedArsc { LoadedArsc() = default; bool LoadTable( const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags); + bool LoadStringPool(const LoadedIdmap* loaded_idmap); std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>(); std::vector<std::unique_ptr<const LoadedPackage>> packages_; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 3d66244646d5..8c614bcf7145 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -53,7 +53,7 @@ constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endi // The version should only be changed when a backwards-incompatible change must be made to the // fabricated overlay file format. Old fabricated overlays must be migrated to the new file format // to prevent losing fabricated overlay data. -constexpr const uint32_t kFabricatedOverlayCurrentVersion = 1; +constexpr const uint32_t kFabricatedOverlayCurrentVersion = 2; // Returns whether or not the path represents a fabricated overlay. bool IsFabricatedOverlay(const std::string& path); diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 444366adf265..c666140765aa 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -2294,10 +2294,10 @@ public class AudioSystem public static int[] DEFAULT_STREAM_VOLUME = new int[] { 4, // STREAM_VOICE_CALL 7, // STREAM_SYSTEM - 5, // STREAM_RING + 5, // STREAM_RING // configured in AudioService by config_audio_notif_vol_default 5, // STREAM_MUSIC 6, // STREAM_ALARM - 5, // STREAM_NOTIFICATION + 5, // STREAM_NOTIFICATION // configured in AudioService by config_audio_ring_vol_default 7, // STREAM_BLUETOOTH_SCO 7, // STREAM_SYSTEM_ENFORCED 5, // STREAM_DTMF diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 35202742bb90..2f9bc1c59624 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -54,7 +54,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; @@ -2019,13 +2018,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // in case authenticators aren't registered yet at this point: mAuthController.addCallback(new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered( - @BiometricAuthenticator.Modality int modality) { + public void onAllAuthenticatorsRegistered() { mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE)); } @Override - public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { + public void onEnrollmentsChanged() { mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE)); } }); @@ -2566,8 +2564,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private boolean isOnlyFaceEnrolled() { - return isFaceAuthEnabledForUser(getCurrentUser()) - && !isUnlockWithFingerprintPossible(getCurrentUser()); + return isFaceEnrolled() + && !getCachedIsUnlockWithFingerprintPossible(sCurrentUser); } private void maybeLogListenerModelData(KeyguardListenModel model) { @@ -2682,7 +2680,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return isUnlockWithFacePossible(userId) || isUnlockWithFingerprintPossible(userId); } - private boolean isUnlockWithFingerprintPossible(int userId) { + @VisibleForTesting + boolean isUnlockWithFingerprintPossible(int userId) { + // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once. mIsUnlockWithFingerprintPossible.put(userId, mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId)); return mIsUnlockWithFingerprintPossible.get(userId); @@ -2704,6 +2704,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * If face hardware is available, user has enrolled and enabled auth via setting. */ public boolean isFaceAuthEnabledForUser(int userId) { + // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once. updateFaceEnrolled(userId); return mIsFaceEnrolled; } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index d6974dfac570..06e1828ef9f4 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -16,7 +16,6 @@ package com.android.keyguard; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static com.android.keyguard.LockIconView.ICON_FINGERPRINT; @@ -30,7 +29,6 @@ import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.AnimatedStateListDrawable; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricSourceType; import android.os.Process; import android.os.VibrationAttributes; @@ -703,17 +701,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsConfig(); - } + public void onAllAuthenticatorsRegistered() { + updateUdfpsConfig(); } @Override - public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsConfig(); - } + public void onEnrollmentsChanged() { + updateUdfpsConfig(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 282f25104c44..47ff59cfc281 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -46,7 +46,6 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; @@ -157,6 +156,25 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } }; + private final IFingerprintAuthenticatorsRegisteredCallback + mFingerprintAuthenticatorsRegisteredCallback = + new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FingerprintSensorPropertiesInternal> sensors) { + mHandler.post(() -> handleAllFingerprintAuthenticatorsRegistered(sensors)); + } + }; + + private final BiometricStateListener mBiometricStateListener = + new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mHandler.post( + () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments)); + } + }; + @VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -231,8 +249,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba List<FingerprintSensorPropertiesInternal> sensors) { mExecution.assertIsMainThread(); if (DEBUG) { - Log.d(TAG, "handleAllFingerprintAuthenticatorsRegistered | sensors: " - + Arrays.toString(sensors.toArray())); + Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString( + sensors.toArray())); } mAllFingerprintAuthenticatorsRegistered = true; mFpProps = sensors; @@ -274,42 +292,15 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba mSidefpsController = mSidefpsControllerFactory.get(); } - mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() { - @Override - public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { - mHandler.post(() -> handleEnrollmentsChanged( - TYPE_FINGERPRINT, userId, sensorId, hasEnrollments)); - } - }); + mFingerprintManager.registerBiometricStateListener(mBiometricStateListener); updateFingerprintLocation(); for (Callback cb : mCallbacks) { - cb.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); + cb.onAllAuthenticatorsRegistered(); } } - private void handleAllFaceAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) { - mExecution.assertIsMainThread(); - if (DEBUG) { - Log.d(TAG, "handleAllFaceAuthenticatorsRegistered | sensors: " + Arrays.toString( - sensors.toArray())); - } - - mFaceManager.registerBiometricStateListener(new BiometricStateListener() { - @Override - public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { - mHandler.post(() -> handleEnrollmentsChanged( - TYPE_FACE, userId, sensorId, hasEnrollments)); - } - }); - - for (Callback cb : mCallbacks) { - cb.onAllAuthenticatorsRegistered(TYPE_FACE); - } - } - - private void handleEnrollmentsChanged(@Modality int modality, int userId, int sensorId, - boolean hasEnrollments) { + private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { mExecution.assertIsMainThread(); Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId + ", hasEnrollments: " + hasEnrollments); @@ -323,7 +314,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } } for (Callback cb : mCallbacks) { - cb.onEnrollmentsChanged(modality); + cb.onEnrollmentsChanged(); } } @@ -709,26 +700,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba if (mFingerprintManager != null) { mFingerprintManager.addAuthenticatorsRegisteredCallback( - new IFingerprintAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FingerprintSensorPropertiesInternal> sensors) { - mHandler.post(() -> - handleAllFingerprintAuthenticatorsRegistered(sensors)); - } - }); - } - if (mFaceManager != null) { - mFaceManager.addAuthenticatorsRegisteredCallback( - new IFaceAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FaceSensorPropertiesInternal> sensors) { - mHandler.post(() -> - handleAllFaceAuthenticatorsRegistered(sensors)); - } - } - ); + mFingerprintAuthenticatorsRegisteredCallback); } mStableDisplaySize = mDisplayManager.getStableDisplaySize(); @@ -1144,13 +1116,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba * Called when authenticators are registered. If authenticators are already * registered before this call, this callback will never be triggered. */ - default void onAllAuthenticatorsRegistered(@Modality int modality) {} + default void onAllAuthenticatorsRegistered() {} /** - * Called when enrollments have changed. This is called after boot and on changes to + * Called when UDFPS enrollments have changed. This is called after boot and on changes to * enrollment. */ - default void onEnrollmentsChanged(@Modality int modality) {} + default void onEnrollmentsChanged() {} /** * Called when the biometric prompt starts showing. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index fd3f6007d8a9..38fab8ffbfad 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -308,7 +308,7 @@ class AuthRippleController @Inject constructor( private val authControllerCallback = object : AuthController.Callback { - override fun onAllAuthenticatorsRegistered(modality: Int) { + override fun onAllAuthenticatorsRegistered() { updateUdfpsDependentParams() updateSensorLocation() } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 7da2cf150147..a9e310d25f9c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -16,15 +16,12 @@ package com.android.systemui.doze; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; -import android.hardware.biometrics.BiometricAuthenticator; import android.os.Handler; import android.util.Log; import android.view.Display; @@ -235,17 +232,13 @@ public class DozeScreenState implements DozeMachine.Part { private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsController(); - } + public void onAllAuthenticatorsRegistered() { + updateUdfpsController(); } @Override - public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsController(); - } + public void onEnrollmentsChanged() { + updateUdfpsController(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 997a6e554364..da6c163b1eea 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -16,8 +16,6 @@ package com.android.systemui.doze; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY; @@ -31,7 +29,6 @@ import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.display.AmbientDisplayConfiguration; import android.net.Uri; import android.os.Handler; @@ -838,17 +835,13 @@ public class DozeSensors { private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsEnrolled(); - } + public void onAllAuthenticatorsRegistered() { + updateUdfpsEnrolled(); } @Override - public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsEnrolled(); - } + public void onEnrollmentsChanged() { + updateUdfpsEnrolled(); } private void updateUdfpsEnrolled() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt index c71eade79cdf..0c49713131e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt @@ -16,7 +16,9 @@ package com.android.systemui.statusbar.notification.collection +import android.app.Notification import android.content.Context +import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.service.notification.StatusBarNotification import android.util.Log @@ -39,17 +41,28 @@ class TargetSdkResolver @Inject constructor( } private fun resolveNotificationSdk(sbn: StatusBarNotification): Int { + val applicationInfo = getApplicationInfoFromExtras(sbn.notification) + ?: getApplicationInfoFromPackageManager(sbn) + + return applicationInfo?.targetSdkVersion ?: 0 + } + + private fun getApplicationInfoFromExtras(notification: Notification): ApplicationInfo? = + notification.extras.getParcelable( + Notification.EXTRA_BUILDER_APPLICATION_INFO, + ApplicationInfo::class.java + ) + + private fun getApplicationInfoFromPackageManager(sbn: StatusBarNotification): ApplicationInfo? { val pmUser = CentralSurfaces.getPackageManagerForUser(context, sbn.user.identifier) - var targetSdk = 0 - // Extract target SDK version. - try { - val info = pmUser.getApplicationInfo(sbn.packageName, 0) - targetSdk = info.targetSdkVersion + + return try { + pmUser.getApplicationInfo(sbn.packageName, 0) } catch (ex: PackageManager.NameNotFoundException) { Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.packageName, ex) + null } - return targetSdk } private val TAG = "TargetSdkResolver" -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index cde30af447f6..c281965550e4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -16,7 +16,6 @@ package com.android.keyguard; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; @@ -582,6 +581,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testTriesToAuthenticate_whenBouncer() { fingerprintIsNotEnrolled(); + faceAuthEnabled(); setKeyguardBouncerVisibility(true); verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); @@ -1006,7 +1006,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN udfps is now enrolled when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); - callback.onEnrollmentsChanged(TYPE_FINGERPRINT); + callback.onEnrollmentsChanged(); // THEN isUdfspEnrolled is TRUE assertThat(mKeyguardUpdateMonitor.isUdfpsEnrolled()).isTrue(); @@ -1219,6 +1219,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse() throws RemoteException { // Face auth should run when the following is true. + faceAuthEnabled(); bouncerFullyVisibleAndNotGoingToSleep(); fingerprintIsNotEnrolled(); keyguardNotGoingAway(); @@ -1285,6 +1286,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse() throws RemoteException { // Preconditions for face auth to run + faceAuthEnabled(); keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); fingerprintIsNotEnrolled(); @@ -1308,6 +1310,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse() throws RemoteException { // Preconditions for face auth to run + faceAuthEnabled(); keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); fingerprintIsNotEnrolled(); @@ -1330,6 +1333,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse() throws RemoteException { // Preconditions for face auth to run + faceAuthEnabled(); keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); fingerprintIsNotEnrolled(); @@ -1375,6 +1379,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue() throws RemoteException { // Preconditions for face auth to run + faceAuthEnabled(); keyguardNotGoingAway(); currentUserIsPrimary(); currentUserDoesNotHaveTrust(); @@ -1540,8 +1545,19 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(anyBoolean())).isEqualTo(true); } + private void faceAuthEnabled() { + // this ensures KeyguardUpdateMonitor updates the cached mIsFaceEnrolled flag using the + // face manager mock wire-up in setup() + mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(mCurrentUserId); + } + private void fingerprintIsNotEnrolled() { when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false); + // This updates the cached fingerprint state. + // There is no straightforward API to update the fingerprint state. + // It currently works updates after enrollment changes because something else invokes + // startListeningForFingerprint(), which internally calls this method. + mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(mCurrentUserId); } private void statusBarShadeIsNotLocked() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index e0d1f7a19130..d158892e4ec5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -60,9 +60,6 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; -import android.hardware.face.FaceSensorProperties; -import android.hardware.face.FaceSensorPropertiesInternal; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -157,9 +154,7 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private InteractionJankMonitor mInteractionJankMonitor; @Captor - ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor; - @Captor - ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; + ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor; @Captor ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor; @Captor @@ -198,38 +193,25 @@ public class AuthControllerTest extends SysuiTestCase { when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point()); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); - when(mFaceManager.isHardwareDetected()).thenReturn(true); - - final List<ComponentInfoInternal> fpComponentInfo = List.of( - new ComponentInfoInternal("faceSensor" /* componentId */, - "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, - "00000001" /* serialNumber */, "" /* softwareVersion */)); - final List<ComponentInfoInternal> faceComponentInfo = List.of( - new ComponentInfoInternal("matchingAlgorithm" /* componentId */, - "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, - "vendor/version/revision" /* softwareVersion */)); - - final List<FingerprintSensorPropertiesInternal> fpProps = List.of( - new FingerprintSensorPropertiesInternal( - 1 /* sensorId */, - SensorProperties.STRENGTH_STRONG, - 1 /* maxEnrollmentsPerUser */, - fpComponentInfo, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - true /* resetLockoutRequireHardwareAuthToken */)); - when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(fpProps); - - final List<FaceSensorPropertiesInternal> faceProps = List.of( - new FaceSensorPropertiesInternal( - 2 /* sensorId */, - SensorProperties.STRENGTH_STRONG, - 1 /* maxEnrollmentsPerUser */, - fpComponentInfo, - FaceSensorProperties.TYPE_RGB, - true /* supportsFaceDetection */, - true /* supportsSelfIllumination */, - true /* resetLockoutRequireHardwareAuthToken */)); - when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps); + + final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); + componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */, + "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, + "00000001" /* serialNumber */, "" /* softwareVersion */)); + componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */, + "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, + "vendor/version/revision" /* softwareVersion */)); + + FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal( + 1 /* sensorId */, + SensorProperties.STRENGTH_STRONG, + 1 /* maxEnrollmentsPerUser */, + componentInfo, + FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + true /* resetLockoutRequireHardwareAuthToken */); + List<FingerprintSensorPropertiesInternal> props = new ArrayList<>(); + props.add(prop); + when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager, @@ -237,15 +219,12 @@ public class AuthControllerTest extends SysuiTestCase { mAuthController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mFpAuthenticatorsRegisteredCaptor.capture()); - verify(mFaceManager).addAuthenticatorsRegisteredCallback( - mFaceAuthenticatorsRegisteredCaptor.capture()); + mAuthenticatorsRegisteredCaptor.capture()); when(mStatusBarStateController.isDozing()).thenReturn(false); verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); - mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps); - mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps); + mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props); // Ensures that the operations posted on the handler get executed. mTestableLooper.processAllMessages(); @@ -258,7 +237,6 @@ public class AuthControllerTest extends SysuiTestCase { throws RemoteException { // This test is sensitive to prior FingerprintManager interactions. reset(mFingerprintManager); - reset(mFaceManager); // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, @@ -268,27 +246,21 @@ public class AuthControllerTest extends SysuiTestCase { authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mFpAuthenticatorsRegisteredCaptor.capture()); - verify(mFaceManager).addAuthenticatorsRegisteredCallback( - mFaceAuthenticatorsRegisteredCaptor.capture()); + mAuthenticatorsRegisteredCaptor.capture()); mTestableLooper.processAllMessages(); verify(mFingerprintManager, never()).registerBiometricStateListener(any()); - verify(mFaceManager, never()).registerBiometricStateListener(any()); - mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); - mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); + mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>()); mTestableLooper.processAllMessages(); verify(mFingerprintManager).registerBiometricStateListener(any()); - verify(mFaceManager).registerBiometricStateListener(any()); } @Test public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException { // This test is sensitive to prior FingerprintManager interactions. reset(mFingerprintManager); - reset(mFaceManager); // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, @@ -298,25 +270,18 @@ public class AuthControllerTest extends SysuiTestCase { authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mFpAuthenticatorsRegisteredCaptor.capture()); - verify(mFaceManager).addAuthenticatorsRegisteredCallback( - mFaceAuthenticatorsRegisteredCaptor.capture()); + mAuthenticatorsRegisteredCaptor.capture()); // Emulates a device with no authenticators (empty list). - mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); - mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); + mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>()); mTestableLooper.processAllMessages(); verify(mFingerprintManager).registerBiometricStateListener( mBiometricStateCaptor.capture()); - verify(mFaceManager).registerBiometricStateListener( - mBiometricStateCaptor.capture()); // Enrollments changed for an unknown sensor. - for (BiometricStateListener listener : mBiometricStateCaptor.getAllValues()) { - listener.onEnrollmentsChanged(0 /* userId */, - 0xbeef /* sensorId */, true /* hasEnrollments */); - } + mBiometricStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */, + 0xbeef /* sensorId */, true /* hasEnrollments */); mTestableLooper.processAllMessages(); // Nothing should crash. @@ -862,3 +827,4 @@ public class AuthControllerTest extends SysuiTestCase { } } } + diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index b33f9a7f3933..9ffc5a57cef6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -16,8 +16,6 @@ package com.android.systemui.doze; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - import static com.android.systemui.doze.DozeLog.REASON_SENSOR_TAP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; @@ -414,7 +412,7 @@ public class DozeSensorsTest extends SysuiTestCase { // WHEN enrollment changes to TRUE when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); - mAuthControllerCallback.onEnrollmentsChanged(TYPE_FINGERPRINT); + mAuthControllerCallback.onEnrollmentsChanged(); // THEN mConfigured = TRUE assertTrue(triggerSensor.mConfigured); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index 5ec6bdf3c00b..24d051508fde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -16,8 +16,6 @@ package com.android.systemui.keyguard; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; @@ -221,7 +219,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { Pair<Float, PointF> udfps = setupUdfps(); // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); + mAuthControllerCallback.onAllAuthenticatorsRegistered(); mDelayableExecutor.runAllReady(); // THEN lock icon view location is updated with the same coordinates as auth controller vals diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt new file mode 100644 index 000000000000..8275c0c24339 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt @@ -0,0 +1,132 @@ +/* + * 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.statusbar.notification.collection + +import android.app.Notification +import android.app.NotificationManager +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.os.Bundle +import android.os.UserHandle +import android.service.notification.NotificationListenerService.Ranking +import android.service.notification.StatusBarNotification +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.withArgCaptor +import junit.framework.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyString +import org.mockito.Mockito.eq +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.`when` as whenever + +private const val SDK_VERSION = 33 +private const val PACKAGE = "pkg" +private const val USER_ID = -1 + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TargetSdkResolverTest : SysuiTestCase() { + private val packageManager: PackageManager = mock() + private val applicationInfo = ApplicationInfo().apply { targetSdkVersion = SDK_VERSION } + + private lateinit var targetSdkResolver: TargetSdkResolver + private lateinit var notifListener: NotifCollectionListener + + @Before + fun setUp() { + targetSdkResolver = TargetSdkResolver(mContext) + mContext.setMockPackageManager(packageManager) + + val notifCollection: CommonNotifCollection = mock() + targetSdkResolver.initialize(notifCollection) + notifListener = withArgCaptor { + verify(notifCollection).addCollectionListener(capture()) + } + } + + @Test + fun resolveFromNotificationExtras() { + val extras = Bundle().apply { + putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, applicationInfo) + } + val notification = Notification().apply { this.extras = extras } + val sbn = createSbn(notification) + val entry = createNotificationEntry(sbn) + + notifListener.onEntryBind(entry, sbn) + + assertEquals(SDK_VERSION, entry.targetSdk) + verifyZeroInteractions(packageManager) + } + + @Test + fun resolveFromPackageManager() { + val sbn = createSbn(Notification()) + val entry = createNotificationEntry(sbn) + whenever(packageManager.getApplicationInfo(anyString(), anyInt())) + .thenReturn(applicationInfo) + + notifListener.onEntryBind(entry, sbn) + + assertEquals(SDK_VERSION, entry.targetSdk) + verify(packageManager).getApplicationInfo(eq(PACKAGE), anyInt()) + } + + @Test + fun resolveFromPackageManager_andPackageManagerCrashes() { + val sbn = createSbn(Notification()) + val entry = createNotificationEntry(sbn) + whenever(packageManager.getApplicationInfo(anyString(), anyInt())) + .thenThrow(PackageManager.NameNotFoundException()) + + notifListener.onEntryBind(entry, sbn) + + assertEquals(0, entry.targetSdk) + verify(packageManager).getApplicationInfo(eq(PACKAGE), anyInt()) + } + + private fun createSbn(notification: Notification) = StatusBarNotification( + PACKAGE, "opPkg", 0, "tag", 0, 0, + notification, UserHandle(USER_ID), "", 0 + ) + + private fun createNotificationEntry(sbn: StatusBarNotification) = + NotificationEntry(sbn, createRanking(sbn.key), 0) + + private fun createRanking(key: String) = Ranking().apply { + populate( + key, + 0, + false, + 0, + 0, + NotificationManager.IMPORTANCE_DEFAULT, + null, null, + null, null, null, true, 0, false, -1, false, null, null, false, false, + false, null, 0, false) + } +} diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 5a20db309f19..785040ea8202 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -63,6 +63,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManagerInternal; @@ -412,10 +413,10 @@ public class AudioService extends IAudioService.Stub protected static int[] MAX_STREAM_VOLUME = new int[] { 5, // STREAM_VOICE_CALL 7, // STREAM_SYSTEM - 7, // STREAM_RING + 7, // STREAM_RING // configured by config_audio_ring_vol_steps 15, // STREAM_MUSIC 7, // STREAM_ALARM - 7, // STREAM_NOTIFICATION + 7, // STREAM_NOTIFICATION // configured by config_audio_notif_vol_steps 15, // STREAM_BLUETOOTH_SCO 7, // STREAM_SYSTEM_ENFORCED 15, // STREAM_DTMF @@ -1116,6 +1117,48 @@ public class AudioService extends IAudioService.Stub MAX_STREAM_VOLUME[AudioSystem.STREAM_SYSTEM]; } + // Read following properties to configure max volume (number of steps) and default volume + // for STREAM_NOTIFICATION and STREAM_RING: + // config_audio_notif_vol_default + // config_audio_notif_vol_steps + // config_audio_ring_vol_default + // config_audio_ring_vol_steps + int[] streams = { AudioSystem.STREAM_NOTIFICATION, AudioSystem.STREAM_RING }; + int[] stepsResId = { com.android.internal.R.integer.config_audio_notif_vol_steps, + com.android.internal.R.integer.config_audio_ring_vol_steps }; + int[] defaultResId = { com.android.internal.R.integer.config_audio_notif_vol_default, + com.android.internal.R.integer.config_audio_ring_vol_default }; + for (int s = 0; s < streams.length; s++) { + try { + final int maxVol = mContext.getResources().getInteger(stepsResId[s]); + if (maxVol <= 0) { + throw new IllegalArgumentException("Invalid negative max volume for stream " + + streams[s]); + } + Log.i(TAG, "Stream " + streams[s] + ": using max vol of " + maxVol); + MAX_STREAM_VOLUME[streams[s]] = maxVol; + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Error querying max vol for stream type " + streams[s], e); + } + try { + final int defaultVol = mContext.getResources().getInteger(defaultResId[s]); + if (defaultVol > MAX_STREAM_VOLUME[streams[s]]) { + throw new IllegalArgumentException("Invalid default volume (" + defaultVol + + ") for stream " + streams[s] + ", greater than max volume of " + + MAX_STREAM_VOLUME[streams[s]]); + } + if (defaultVol < MIN_STREAM_VOLUME[streams[s]]) { + throw new IllegalArgumentException("Invalid default volume (" + defaultVol + + ") for stream " + streams[s] + ", lower than min volume of " + + MIN_STREAM_VOLUME[streams[s]]); + } + Log.i(TAG, "Stream " + streams[s] + ": using default vol of " + defaultVol); + AudioSystem.DEFAULT_STREAM_VOLUME[streams[s]] = defaultVol; + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Error querying default vol for stream type " + streams[s], e); + } + } + if (looper == null) { createAudioSystemThread(); } else { diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java deleted file mode 100644 index 0f1fe68ad1d7..000000000000 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java +++ /dev/null @@ -1,61 +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.biometrics.sensors; - -import android.annotation.NonNull; -import android.hardware.biometrics.SensorPropertiesInternal; -import android.util.proto.ProtoOutputStream; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; - -/** - * Common attributes for all biometric service providers. - * - * @param <T> Internal settings type. - */ -public interface BiometricServiceProvider<T extends SensorPropertiesInternal> { - - /** Checks if the specified sensor is owned by this provider. */ - boolean containsSensor(int sensorId); - - /** All sensor properties. */ - @NonNull - List<T> getSensorProperties(); - - /** Properties for the given sensor id. */ - @NonNull - T getSensorProperties(int sensorId); - - boolean isHardwareDetected(int sensorId); - - /** If the user has any enrollments for the given sensor. */ - boolean hasEnrollments(int sensorId, int userId); - - long getAuthenticatorId(int sensorId, int userId); - - @LockoutTracker.LockoutMode - int getLockoutModeForUser(int sensorId, int userId); - - void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, - boolean clearSchedulerBuffer); - - void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); - - void dumpInternal(int sensorId, @NonNull PrintWriter pw); -} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java deleted file mode 100644 index 7574523f0662..000000000000 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java +++ /dev/null @@ -1,235 +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.biometrics.sensors; - - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.hardware.biometrics.IBiometricAuthenticator; -import android.hardware.biometrics.IBiometricService; -import android.hardware.biometrics.SensorPropertiesInternal; -import android.os.Handler; -import android.os.IInterface; -import android.os.Process; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.util.Pair; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.ServiceThread; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Supplier; - -/** - * Container for all BiometricServiceProvider implementations. - * - * @param <T> The service provider type. - * @param <P> The internal properties type. - * @param <C> The registration callback for {@link #invokeRegisteredCallback(IInterface, List)}. - */ -public abstract class BiometricServiceRegistry<T extends BiometricServiceProvider<P>, - P extends SensorPropertiesInternal, - C extends IInterface> { - - private static final String TAG = "BiometricServiceRegistry"; - - // Volatile so they can be read without a lock once all services are registered. - // But, ideally remove this and provide immutable copies via the callback instead. - @Nullable - private volatile List<T> mServiceProviders; - @Nullable - private volatile List<P> mAllProps; - - @NonNull - private final Supplier<IBiometricService> mBiometricServiceSupplier; - @NonNull - private final RemoteCallbackList<C> mRegisteredCallbacks = new RemoteCallbackList<>(); - - public BiometricServiceRegistry(@NonNull Supplier<IBiometricService> biometricSupplier) { - mBiometricServiceSupplier = biometricSupplier; - } - - /** - * Register an implementation by creating a new authenticator and initializing it via - * {@link IBiometricService#registerAuthenticator(int, int, int, IBiometricAuthenticator)} - * using the given properties. - * - * @param service service to register with - * @param props internal properties to initialize the authenticator - */ - protected abstract void registerService(@NonNull IBiometricService service, @NonNull P props); - - /** - * Invoke the callback to notify clients that all authenticators have been registered. - * - * @param callback callback to invoke - * @param allProps properties of all authenticators - */ - protected abstract void invokeRegisteredCallback(@NonNull C callback, - @NonNull List<P> allProps) throws RemoteException; - - /** - * Register all authenticators in a background thread. - * - * @param serviceProvider Supplier function that will be invoked on the background thread. - */ - public void registerAll(Supplier<List<T>> serviceProvider) { - // Some HAL might not be started before the system service and will cause the code below - // to wait, and some of the operations below might take a significant amount of time to - // complete (calls to the HALs). To avoid blocking the rest of system server we put - // this on a background thread. - final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, - true /* allowIo */); - thread.start(); - final Handler handler = new Handler(thread.getLooper()); - handler.post(() -> registerAllInBackground(serviceProvider)); - thread.quitSafely(); - } - - /** Register authenticators now, only called by {@link #registerAll(Supplier).} */ - @VisibleForTesting - public void registerAllInBackground(Supplier<List<T>> serviceProvider) { - List<T> providers = serviceProvider.get(); - if (providers == null) { - providers = new ArrayList<>(); - } - - final IBiometricService biometricService = mBiometricServiceSupplier.get(); - if (biometricService == null) { - throw new IllegalStateException("biometric service cannot be null"); - } - - // Register each sensor individually with BiometricService - final List<P> allProps = new ArrayList<>(); - for (T provider : providers) { - final List<P> props = provider.getSensorProperties(); - for (P prop : props) { - registerService(biometricService, prop); - } - allProps.addAll(props); - } - - finishRegistration(providers, allProps); - } - - private synchronized void finishRegistration( - @NonNull List<T> providers, @NonNull List<P> allProps) { - mServiceProviders = Collections.unmodifiableList(providers); - mAllProps = Collections.unmodifiableList(allProps); - broadcastAllAuthenticatorsRegistered(); - } - - /** - * Add a callback that will be invoked once the work from {@link #registerAll(Supplier)} - * has finished registering all providers (executes immediately if already done). - * - * @param callback registration callback - */ - public synchronized void addAllRegisteredCallback(@Nullable C callback) { - if (callback == null) { - Slog.e(TAG, "addAllRegisteredCallback, callback is null"); - return; - } - - final boolean registered = mRegisteredCallbacks.register(callback); - final boolean allRegistered = mServiceProviders != null; - if (registered && allRegistered) { - broadcastAllAuthenticatorsRegistered(); - } else if (!registered) { - Slog.e(TAG, "addAllRegisteredCallback failed to register callback"); - } - } - - private synchronized void broadcastAllAuthenticatorsRegistered() { - final int n = mRegisteredCallbacks.beginBroadcast(); - for (int i = 0; i < n; ++i) { - final C cb = mRegisteredCallbacks.getBroadcastItem(i); - try { - invokeRegisteredCallback(cb, mAllProps); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception in broadcastAllAuthenticatorsRegistered", e); - } finally { - mRegisteredCallbacks.unregister(cb); - } - } - mRegisteredCallbacks.finishBroadcast(); - } - - /** - * Get a list of registered providers. - * - * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. - */ - @NonNull - public List<T> getProviders() { - return mServiceProviders != null ? mServiceProviders : Collections.emptyList(); - } - - /** - * Gets the provider for given sensor id or null if not registered. - * - * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. - */ - @Nullable - public T getProviderForSensor(int sensorId) { - if (mServiceProviders != null) { - for (T provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return provider; - } - } - } - return null; - } - - /** - * For devices with only a single provider, returns that provider. - * If no providers, or multiple providers exist, returns null. - * - * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. - */ - @Nullable - public Pair<Integer, T> getSingleProvider() { - if (mAllProps == null || mAllProps.size() != 1) { - Slog.e(TAG, "Multiple sensors found: " + mAllProps.size()); - return null; - } - - final int sensorId = mAllProps.get(0).sensorId; - final T provider = getProviderForSensor(sensorId); - if (provider != null) { - return new Pair<>(sensorId, provider); - } - - Slog.e(TAG, "Single sensor, but provider not found"); - return null; - } - - /** - * Get the properties for all providers. - * - * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. - */ - @NonNull - public List<P> getAllProperties() { - return mAllProps != null ? mAllProps : Collections.emptyList(); - } -} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java index f8543162f95e..0d789f7a1840 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java @@ -23,64 +23,32 @@ import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE; import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.IBiometricStateListener; -import android.hardware.biometrics.SensorPropertiesInternal; import android.os.RemoteException; -import android.os.UserManager; import android.util.Slog; import com.android.server.biometrics.Utils; -import java.util.Collections; -import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * A callback for receiving notifications about biometric sensor state changes. - * - * @param <T> service provider type - * @param <P> internal property type */ -public class BiometricStateCallback<T extends BiometricServiceProvider<P>, - P extends SensorPropertiesInternal> implements ClientMonitorCallback { +public class BiometricStateCallback implements ClientMonitorCallback { private static final String TAG = "BiometricStateCallback"; @NonNull - private final CopyOnWriteArrayList<IBiometricStateListener> mBiometricStateListeners = - new CopyOnWriteArrayList<>(); - @NonNull - private final UserManager mUserManager; - @BiometricStateListener.State - private int mBiometricState; - @NonNull - private List<T> mProviders = List.of(); + private final CopyOnWriteArrayList<IBiometricStateListener> + mBiometricStateListeners = new CopyOnWriteArrayList<>(); - /** - * Create a new callback that must be {@link #start(List)}ed. - * - * @param userManager user manager - */ - public BiometricStateCallback(@NonNull UserManager userManager) { - mBiometricState = STATE_IDLE; - mUserManager = userManager; - } + private @BiometricStateListener.State int mBiometricState; - /** - * This should be called when the service has been initialized and all providers are ready. - * - * @param allProviders all registered biometric service providers - */ - public synchronized void start(@NonNull List<T> allProviders) { - mProviders = Collections.unmodifiableList(allProviders); - broadcastCurrentEnrollmentState(null /* listener */); + public BiometricStateCallback() { + mBiometricState = STATE_IDLE; } - /** Get the current state. */ - @BiometricStateListener.State public int getBiometricState() { return mBiometricState; } @@ -152,43 +120,23 @@ public class BiometricStateCallback<T extends BiometricServiceProvider<P>, } /** - * Enables clients to register a BiometricStateListener. For example, this is used to forward - * fingerprint sensor state changes to SideFpsEventHandler. - * - * @param listener listener to register + * This should be invoked when: + * 1) Enrolled --> None-enrolled + * 2) None-enrolled --> enrolled + * 3) HAL becomes ready + * 4) Listener is registered */ - public synchronized void registerBiometricStateListener( - @NonNull IBiometricStateListener listener) { - mBiometricStateListeners.add(listener); - broadcastCurrentEnrollmentState(listener); - } - - private synchronized void broadcastCurrentEnrollmentState( - @Nullable IBiometricStateListener listener) { - for (T provider : mProviders) { - for (SensorPropertiesInternal prop : provider.getSensorProperties()) { - for (UserInfo userInfo : mUserManager.getAliveUsers()) { - final boolean enrolled = provider.hasEnrollments(prop.sensorId, userInfo.id); - if (listener != null) { - notifyEnrollmentStateChanged( - listener, userInfo.id, prop.sensorId, enrolled); - } else { - notifyAllEnrollmentStateChanged( - userInfo.id, prop.sensorId, enrolled); - } - } - } - } - } - - private void notifyAllEnrollmentStateChanged(int userId, int sensorId, + public void notifyAllEnrollmentStateChanged(int userId, int sensorId, boolean hasEnrollments) { for (IBiometricStateListener listener : mBiometricStateListeners) { notifyEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments); } } - private void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener, + /** + * Notifies the listener of enrollment state changes. + */ + public void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener, int userId, int sensorId, boolean hasEnrollments) { try { listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments); @@ -196,4 +144,14 @@ public class BiometricStateCallback<T extends BiometricServiceProvider<P>, Slog.e(TAG, "Remote exception", e); } } + + /** + * Enables clients to register a BiometricStateListener. For example, this is used to forward + * fingerprint sensor state changes to SideFpsEventHandler. + * + * @param listener + */ + public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + mBiometricStateListeners.add(listener); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 271bce9890c6..79e65cc6d2e5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -17,18 +17,20 @@ package com.android.server.biometrics.sensors.face; import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.MANAGE_FACE; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; +import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; -import android.hardware.biometrics.IBiometricStateListener; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; @@ -37,18 +39,18 @@ import android.hardware.biometrics.face.SensorProps; import android.hardware.face.Face; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.FaceServiceReceiver; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.face.IFaceService; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; import android.os.NativeHandle; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.UserHandle; -import android.os.UserManager; import android.util.Pair; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -56,10 +58,10 @@ import android.view.Surface; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; +import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; -import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -86,10 +88,51 @@ public class FaceService extends SystemService { private final LockoutResetDispatcher mLockoutResetDispatcher; private final LockPatternUtils mLockPatternUtils; @NonNull - private final FaceServiceRegistry mRegistry; + private final List<ServiceProvider> mServiceProviders; + + @Nullable + private ServiceProvider getProviderForSensor(int sensorId) { + for (ServiceProvider provider : mServiceProviders) { + if (provider.containsSensor(sensorId)) { + return provider; + } + } + return null; + } + + /** + * For devices with only a single provider, returns that provider. If no providers, or multiple + * providers exist, returns null. + */ + @Nullable + private Pair<Integer, ServiceProvider> getSingleProvider() { + final List<FaceSensorPropertiesInternal> properties = getSensorProperties(); + if (properties.size() != 1) { + Slog.e(TAG, "Multiple sensors found: " + properties.size()); + return null; + } + + // Theoretically we can just return the first provider, but maybe this is easier to + // understand. + final int sensorId = properties.get(0).sensorId; + for (ServiceProvider provider : mServiceProviders) { + if (provider.containsSensor(sensorId)) { + return new Pair<>(sensorId, provider); + } + } + + Slog.e(TAG, "Single sensor, but provider not found"); + return null; + } + @NonNull - private final BiometricStateCallback<ServiceProvider, FaceSensorPropertiesInternal> - mBiometricStateCallback; + private List<FaceSensorPropertiesInternal> getSensorProperties() { + final List<FaceSensorPropertiesInternal> properties = new ArrayList<>(); + for (ServiceProvider provider : mServiceProviders) { + properties.addAll(provider.getSensorProperties()); + } + return properties; + } /** * Receives the incoming binder calls from FaceManager. @@ -99,7 +142,8 @@ public class FaceService extends SystemService { @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId); @@ -112,8 +156,9 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) { + final ProtoOutputStream proto = new ProtoOutputStream(); - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider != null) { provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer); } @@ -125,14 +170,16 @@ public class FaceService extends SystemService { @Override // Binder call public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal( String opPackageName) { - return mRegistry.getAllProperties(); + + return FaceService.this.getSensorProperties(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public FaceSensorPropertiesInternal getSensorProperties(int sensorId, @NonNull String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId + ", caller: " + opPackageName); @@ -146,7 +193,8 @@ public class FaceService extends SystemService { @Override // Binder call public void generateChallenge(IBinder token, int sensorId, int userId, IFaceServiceReceiver receiver, String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); return; @@ -159,7 +207,8 @@ public class FaceService extends SystemService { @Override // Binder call public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); return; @@ -173,7 +222,8 @@ public class FaceService extends SystemService { public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken, final IFaceServiceReceiver receiver, final String opPackageName, final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); return -1; @@ -195,7 +245,8 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC) @Override // Binder call public void cancelEnrollment(final IBinder token, long requestId) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelEnrollment"); return; @@ -209,6 +260,7 @@ public class FaceService extends SystemService { public long authenticate(final IBinder token, final long operationId, int userId, final IFaceServiceReceiver receiver, final String opPackageName, boolean isKeyguardBypassEnabled) { + // TODO(b/152413782): If the sensor supports face detect and the device is encrypted or // lockdown, something wrong happened. See similar path in FingerprintService. @@ -221,7 +273,7 @@ public class FaceService extends SystemService { // permission. final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName); - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); return -1; @@ -249,7 +301,7 @@ public class FaceService extends SystemService { return -1; } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFace"); return -1; @@ -266,7 +318,8 @@ public class FaceService extends SystemService { IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for prepareForAuthentication"); return; @@ -283,7 +336,8 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public void startPreparedClient(int sensorId, int cookie) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for startPreparedClient"); return; @@ -296,7 +350,8 @@ public class FaceService extends SystemService { @Override // Binder call public void cancelAuthentication(final IBinder token, final String opPackageName, final long requestId) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthentication"); return; @@ -315,7 +370,7 @@ public class FaceService extends SystemService { return; } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelFaceDetect"); return; @@ -328,7 +383,8 @@ public class FaceService extends SystemService { @Override // Binder call public void cancelAuthenticationFromService(int sensorId, final IBinder token, final String opPackageName, final long requestId) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthenticationFromService"); return; @@ -341,7 +397,8 @@ public class FaceService extends SystemService { @Override // Binder call public void remove(final IBinder token, final int faceId, final int userId, final IFaceServiceReceiver receiver, final String opPackageName) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for remove"); return; @@ -355,6 +412,7 @@ public class FaceService extends SystemService { @Override // Binder call public void removeAll(final IBinder token, final int userId, final IFaceServiceReceiver receiver, final String opPackageName) { + final FaceServiceReceiver internalReceiver = new FaceServiceReceiver() { int sensorsFinishedRemoving = 0; final int numSensors = getSensorPropertiesInternal( @@ -374,7 +432,7 @@ public class FaceService extends SystemService { // This effectively iterates through all sensors, but has to do so by finding all // sensors under each provider. - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { List<FaceSensorPropertiesInternal> props = provider.getSensorProperties(); for (FaceSensorPropertiesInternal prop : props) { provider.scheduleRemoveAll(prop.sensorId, token, userId, internalReceiver, @@ -409,27 +467,27 @@ public class FaceService extends SystemService { try { if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoState(props.sensorId, proto, false); } } proto.flush(); } else if (args.length > 0 && "--proto".equals(args[0])) { - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoMetrics(props.sensorId, fd); } } } else if (args.length > 1 && "--hal".equals(args[0])) { - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpHal(props.sensorId, fd, Arrays.copyOfRange(args, 1, args.length, args.getClass())); } } } else { - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { pw.println("Dumping for sensorId: " + props.sensorId + ", provider: " + provider.getClass().getSimpleName()); @@ -446,9 +504,10 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean isHardwareDetected(int sensorId, String opPackageName) { + final long token = Binder.clearCallingIdentity(); try { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName); return false; @@ -462,11 +521,12 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public List<Face> getEnrolledFaces(int sensorId, int userId, String opPackageName) { + if (userId != UserHandle.getCallingUserId()) { Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName); return Collections.emptyList(); @@ -478,11 +538,12 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean hasEnrolledFaces(int sensorId, int userId, String opPackageName) { + if (userId != UserHandle.getCallingUserId()) { Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName); return false; @@ -494,7 +555,8 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getLockoutModeForUser"); return LockoutTracker.LOCKOUT_NONE; @@ -507,7 +569,8 @@ public class FaceService extends SystemService { @Override public void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); return; @@ -519,7 +582,7 @@ public class FaceService extends SystemService { @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getAuthenticatorId"); return 0; @@ -532,7 +595,8 @@ public class FaceService extends SystemService { @Override // Binder call public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken, String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; @@ -546,7 +610,8 @@ public class FaceService extends SystemService { public void setFeature(final IBinder token, int userId, int feature, boolean enabled, final byte[] hardwareAuthToken, IFaceServiceReceiver receiver, final String opPackageName) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for setFeature"); return; @@ -560,7 +625,8 @@ public class FaceService extends SystemService { @Override public void getFeature(final IBinder token, int userId, int feature, IFaceServiceReceiver receiver, final String opPackageName) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for getFeature"); return; @@ -570,14 +636,18 @@ public class FaceService extends SystemService { new ClientMonitorCallbackConverter(receiver), opPackageName); } - private List<ServiceProvider> getAidlProviders() { - final List<ServiceProvider> providers = new ArrayList<>(); + private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) { + for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) { + mServiceProviders.add( + Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher)); + } + } + private void addAidlProviders() { final String[] instances = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR); if (instances == null || instances.length == 0) { - return providers; + return; } - for (String instance : instances) { final String fqName = IFace.DESCRIPTOR + "/" + instance; final IFace face = IFace.Stub.asInterface( @@ -590,41 +660,53 @@ public class FaceService extends SystemService { final SensorProps[] props = face.getSensorProps(); final FaceProvider provider = new FaceProvider(getContext(), props, instance, mLockoutResetDispatcher, BiometricContext.getInstance(getContext())); - providers.add(provider); + mServiceProviders.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } } - - return providers; } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public void registerAuthenticators( @NonNull List<FaceSensorPropertiesInternal> hidlSensors) { - mRegistry.registerAll(() -> { - final List<ServiceProvider> providers = new ArrayList<>(); - for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) { - providers.add( - Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher)); + + // Some HAL might not be started before the system service and will cause the code below + // to wait, and some of the operations below might take a significant amount of time to + // complete (calls to the HALs). To avoid blocking the rest of system server we put + // this on a background thread. + final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, + true /* allowIo */); + thread.start(); + final Handler handler = new Handler(thread.getLooper()); + + handler.post(() -> { + addHidlProviders(hidlSensors); + addAidlProviders(); + + final IBiometricService biometricService = IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE)); + + // Register each sensor individually with BiometricService + for (ServiceProvider provider : mServiceProviders) { + final List<FaceSensorPropertiesInternal> props = provider.getSensorProperties(); + for (FaceSensorPropertiesInternal prop : props) { + final int sensorId = prop.sensorId; + final @BiometricManager.Authenticators.Types int strength = + Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength); + final FaceAuthenticator authenticator = new FaceAuthenticator( + mServiceWrapper, sensorId); + try { + biometricService.registerAuthenticator(sensorId, TYPE_FACE, strength, + authenticator); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId); + } + } } - providers.addAll(getAidlProviders()); - return providers; }); } - - @Override - public void addAuthenticatorsRegisteredCallback( - IFaceAuthenticatorsRegisteredCallback callback) { - Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - mRegistry.addAllRegisteredCallback(callback); - } - - @Override - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - mBiometricStateCallback.registerBiometricStateListener(listener); - } } public FaceService(Context context) { @@ -632,16 +714,7 @@ public class FaceService extends SystemService { mServiceWrapper = new FaceServiceWrapper(); mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); - mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); - mRegistry = new FaceServiceRegistry(mServiceWrapper, - () -> IBiometricService.Stub.asInterface( - ServiceManager.getService(Context.BIOMETRIC_SERVICE))); - mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) { - mBiometricStateCallback.start(mRegistry.getProviders()); - } - }); + mServiceProviders = new ArrayList<>(); } @Override @@ -679,7 +752,7 @@ public class FaceService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { Slog.i(TAG, "Sync virtual enrollments"); final int userId = ActivityManager.getCurrentUser(); - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */, true /* favorHalEnrollments */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java deleted file mode 100644 index 0f0a81d24473..000000000000 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java +++ /dev/null @@ -1,71 +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.biometrics.sensors.face; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.hardware.biometrics.BiometricManager; -import android.hardware.biometrics.IBiometricService; -import android.hardware.face.FaceSensorPropertiesInternal; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; -import android.hardware.face.IFaceService; -import android.os.RemoteException; -import android.util.Slog; - -import com.android.server.biometrics.Utils; -import com.android.server.biometrics.sensors.BiometricServiceRegistry; - -import java.util.List; -import java.util.function.Supplier; - -/** Registry for {@link IFaceService} providers. */ -public class FaceServiceRegistry extends BiometricServiceRegistry<ServiceProvider, - FaceSensorPropertiesInternal, IFaceAuthenticatorsRegisteredCallback> { - - private static final String TAG = "FaceServiceRegistry"; - - @NonNull - private final IFaceService mService; - - /** Creates a new registry tied to the given service. */ - public FaceServiceRegistry(@NonNull IFaceService service, - @Nullable Supplier<IBiometricService> biometricSupplier) { - super(biometricSupplier); - mService = service; - } - - @Override - protected void registerService(@NonNull IBiometricService service, - @NonNull FaceSensorPropertiesInternal props) { - @BiometricManager.Authenticators.Types final int strength = - Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength); - try { - service.registerAuthenticator(props.sensorId, TYPE_FACE, strength, - new FaceAuthenticator(mService, props.sensorId)); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId); - } - } - - @Override - protected void invokeRegisteredCallback(@NonNull IFaceAuthenticatorsRegisteredCallback callback, - @NonNull List<FaceSensorPropertiesInternal> allProps) throws RemoteException { - callback.onAllAuthenticatorsRegistered(allProps); - } -} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 4efaedbd5530..6f98365332e2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -26,13 +26,15 @@ import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.IBinder; +import android.util.proto.ProtoOutputStream; import android.view.Surface; -import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutTracker; import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.List; /** @@ -54,11 +56,24 @@ import java.util.List; * to check (e.g. via {@link FaceManager#getSensorPropertiesInternal()}) that the code path isn't * taken. ServiceProviders will provide a no-op for unsupported operations to fail safely. */ -public interface ServiceProvider extends BiometricServiceProvider<FaceSensorPropertiesInternal> { +public interface ServiceProvider { + /** + * Checks if the specified sensor is owned by this provider. + */ + boolean containsSensor(int sensorId); + + @NonNull + List<FaceSensorPropertiesInternal> getSensorProperties(); + + @NonNull + FaceSensorPropertiesInternal getSensorProperties(int sensorId); @NonNull List<Face> getEnrolledFaces(int sensorId, int userId); + @LockoutTracker.LockoutMode + int getLockoutModeForUser(int sensorId, int userId); + /** * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to be * invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} @@ -69,6 +84,10 @@ public interface ServiceProvider extends BiometricServiceProvider<FaceSensorProp + " this method"); } + long getAuthenticatorId(int sensorId, int userId); + + boolean isHardwareDetected(int sensorId); + void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFaceServiceReceiver receiver, String opPackageName); @@ -123,6 +142,13 @@ public interface ServiceProvider extends BiometricServiceProvider<FaceSensorProp void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments); + void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, + boolean clearSchedulerBuffer); + + void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); + + void dumpInternal(int sensorId, @NonNull PrintWriter pw); + @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName); 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 6bff179e8eb7..19d54c84c706 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 @@ -285,11 +285,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } @Override - public boolean hasEnrollments(int sensorId, int userId) { - return !getEnrolledFaces(sensorId, userId).isEmpty(); - } - - @Override public void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback) { mHandler.post(() -> { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index c0a119ff5f1e..65289122747c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -484,11 +484,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } @Override - public boolean hasEnrollments(int sensorId, int userId) { - return !getEnrolledFaces(sensorId, userId).isEmpty(); - } - - @Override @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) { return mLockoutTracker.getLockoutModeForUser(userId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 7e2742edd47a..2ba449ae089e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -17,11 +17,14 @@ package com.android.server.biometrics.sensors.fingerprint; import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.MANAGE_FINGERPRINT; +import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; @@ -33,6 +36,8 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -60,6 +65,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -73,9 +79,11 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; +import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; @@ -107,32 +115,74 @@ public class FingerprintService extends SystemService { protected static final String TAG = "FingerprintService"; + private final Object mLock = new Object(); private final AppOpsManager mAppOps; private final LockoutResetDispatcher mLockoutResetDispatcher; private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; private final LockPatternUtils mLockPatternUtils; - @NonNull - private final BiometricContext mBiometricContext; - @NonNull - private final Supplier<String[]> mAidlInstanceNameSupplier; - @NonNull - private final Function<String, IFingerprint> mIFingerprintProvider; - @NonNull - private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal> - mBiometricStateCallback; - @NonNull - private final Handler mHandler; - @NonNull - private final FingerprintServiceRegistry mRegistry; + @NonNull private final List<ServiceProvider> mServiceProviders; + @NonNull private final BiometricStateCallback mBiometricStateCallback; + @NonNull private final Handler mHandler; + @NonNull private final BiometricContext mBiometricContext; + @NonNull private final Supplier<IBiometricService> mBiometricServiceSupplier; + @NonNull private final Function<String, IFingerprint> mIFingerprintProvider; + + @GuardedBy("mLock") + @NonNull private final RemoteCallbackList<IFingerprintAuthenticatorsRegisteredCallback> + mAuthenticatorsRegisteredCallbacks; + + @GuardedBy("mLock") + @NonNull private final List<FingerprintSensorPropertiesInternal> mSensorProps; + + /** + * Registers BiometricStateListener in list stored by FingerprintService + * @param listener new BiometricStateListener being added + */ + public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + mBiometricStateCallback.registerBiometricStateListener(listener); + broadcastCurrentEnrollmentState(listener); + } - /** Receives the incoming binder calls from FingerprintManager. */ - @VisibleForTesting - final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() { + /** + * @param listener if non-null, notifies only this listener. if null, notifies all listeners + * in {@link BiometricStateCallback}. This is slightly ugly, but reduces + * redundant code. + */ + private void broadcastCurrentEnrollmentState(@Nullable IBiometricStateListener listener) { + final UserManager um = UserManager.get(getContext()); + synchronized (mLock) { + // Update the new listener with current state of all sensors + for (FingerprintSensorPropertiesInternal prop : mSensorProps) { + final ServiceProvider provider = getProviderForSensor(prop.sensorId); + for (UserInfo userInfo : um.getAliveUsers()) { + final boolean enrolled = !provider + .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty(); + + // Defer this work and allow the loop to release the lock sooner + mHandler.post(() -> { + if (listener != null) { + mBiometricStateCallback.notifyEnrollmentStateChanged( + listener, userInfo.id, prop.sensorId, enrolled); + } else { + mBiometricStateCallback.notifyAllEnrollmentStateChanged( + userInfo.id, prop.sensorId, enrolled); + } + }); + } + } + } + } + + /** + * Receives the incoming binder calls from FingerprintManager. + */ + private final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() { @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC) @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId); @@ -145,8 +195,9 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) { + final ProtoOutputStream proto = new ProtoOutputStream(); - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider != null) { provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer); } @@ -161,14 +212,16 @@ public class FingerprintService extends SystemService { != PackageManager.PERMISSION_GRANTED) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } - return mRegistry.getAllProperties(); + + return FingerprintService.this.getSensorProperties(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId, @NonNull String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId + ", caller: " + opPackageName); @@ -181,7 +234,8 @@ public class FingerprintService extends SystemService { @Override // Binder call public void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); return; @@ -194,7 +248,8 @@ public class FingerprintService extends SystemService { @Override // Binder call public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); return; @@ -209,7 +264,8 @@ public class FingerprintService extends SystemService { public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); return -1; @@ -222,7 +278,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_FINGERPRINT) @Override // Binder call public void cancelEnrollment(final IBinder token, long requestId) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelEnrollment"); return; @@ -282,10 +339,10 @@ public class FingerprintService extends SystemService { final Pair<Integer, ServiceProvider> provider; if (sensorId == FingerprintManager.SENSOR_ID_ANY) { - provider = mRegistry.getSingleProvider(); + provider = getSingleProvider(); } else { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - provider = new Pair<>(sensorId, mRegistry.getProviderForSensor(sensorId)); + provider = new Pair<>(sensorId, getProviderForSensor(sensorId)); } if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); @@ -317,6 +374,7 @@ public class FingerprintService extends SystemService { final IFingerprintServiceReceiver receiver, final String opPackageName, boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException { + final Context context = getUiContext(); final Context promptContext = context.createPackageContextAsUser( opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(uId)); @@ -410,7 +468,7 @@ public class FingerprintService extends SystemService { return -1; } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFingerprint"); return -1; @@ -426,7 +484,8 @@ public class FingerprintService extends SystemService { public void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for prepareForAuthentication"); return; @@ -442,7 +501,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC) @Override // Binder call public void startPreparedClient(int sensorId, int cookie) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for startPreparedClient"); return; @@ -472,7 +532,7 @@ public class FingerprintService extends SystemService { return; } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthentication"); return; @@ -493,7 +553,7 @@ public class FingerprintService extends SystemService { // For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as // cancelling authentication. - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelFingerprintDetect"); return; @@ -506,9 +566,11 @@ public class FingerprintService extends SystemService { @Override // Binder call public void cancelAuthenticationFromService(final int sensorId, final IBinder token, final String opPackageName, final long requestId) { + + Slog.d(TAG, "cancelAuthenticationFromService, sensorId: " + sensorId); - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthenticationFromService"); return; @@ -521,7 +583,8 @@ public class FingerprintService extends SystemService { @Override // Binder call public void remove(final IBinder token, final int fingerId, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for remove"); return; @@ -554,7 +617,7 @@ public class FingerprintService extends SystemService { // This effectively iterates through all sensors, but has to do so by finding all // sensors under each provider. - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties(); for (FingerprintSensorPropertiesInternal prop : props) { provider.scheduleRemoveAll(prop.sensorId, token, internalReceiver, userId, @@ -589,7 +652,7 @@ public class FingerprintService extends SystemService { try { if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoState(props.sensorId, proto, false); @@ -597,14 +660,14 @@ public class FingerprintService extends SystemService { } proto.flush(); } else if (args.length > 0 && "--proto".equals(args[0])) { - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoMetrics(props.sensorId, fd); } } } else { - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { pw.println("Dumping for sensorId: " + props.sensorId @@ -635,7 +698,7 @@ public class FingerprintService extends SystemService { final long token = Binder.clearCallingIdentity(); try { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetectedDeprecated, caller: " + opPackageName); @@ -650,7 +713,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean isHardwareDetected(int sensorId, String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName); return false; @@ -666,7 +730,7 @@ public class FingerprintService extends SystemService { return; } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for rename"); return; @@ -717,7 +781,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for hasEnrolledFingerprints, caller: " + opPackageName); return false; @@ -729,7 +794,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getLockoutModeForUser"); return LockoutTracker.LOCKOUT_NONE; @@ -741,7 +807,8 @@ public class FingerprintService extends SystemService { @Override public void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); return; @@ -752,7 +819,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getAuthenticatorId"); return 0; @@ -764,7 +832,8 @@ public class FingerprintService extends SystemService { @Override // Binder call public void resetLockout(IBinder token, int sensorId, int userId, @Nullable byte[] hardwareAuthToken, String opPackageName) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; @@ -795,38 +864,55 @@ public class FingerprintService extends SystemService { @Override // Binder call public void registerAuthenticators( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { - mRegistry.registerAll(() -> { - final List<ServiceProvider> providers = new ArrayList<>(); - providers.addAll(getHidlProviders(hidlSensors)); + + // Some HAL might not be started before the system service and will cause the code below + // to wait, and some of the operations below might take a significant amount of time to + // complete (calls to the HALs). To avoid blocking the rest of system server we put + // this on a background thread. + final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, + true /* allowIo */); + thread.start(); + final Handler handler = new Handler(thread.getLooper()); + handler.post(() -> { List<String> aidlSensors = new ArrayList<>(); - final String[] instances = mAidlInstanceNameSupplier.get(); + final String[] instances = + ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR); if (instances != null) { aidlSensors.addAll(Lists.newArrayList(instances)); } - providers.addAll(getAidlProviders( - Utils.filterAvailableHalInstances(getContext(), aidlSensors))); - return providers; + registerAuthenticatorsForService(aidlSensors, hidlSensors); }); + thread.quitSafely(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( IFingerprintAuthenticatorsRegisteredCallback callback) { - mRegistry.addAllRegisteredCallback(callback); - } + if (callback == null) { + Slog.e(TAG, "addAuthenticatorsRegisteredCallback, callback is null"); + return; + } - @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) - @Override - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - mBiometricStateCallback.registerBiometricStateListener(listener); + final boolean registered; + final boolean hasSensorProps; + synchronized (mLock) { + registered = mAuthenticatorsRegisteredCallbacks.register(callback); + hasSensorProps = !mSensorProps.isEmpty(); + } + if (registered && hasSensorProps) { + broadcastAllAuthenticatorsRegistered(); + } else if (!registered) { + Slog.e(TAG, "addAuthenticatorsRegisteredCallback failed to register callback"); + } } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId); return; @@ -837,7 +923,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPointerUp(long requestId, int sensorId) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId); return; @@ -848,7 +935,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onUiReady(long requestId, int sensorId) { - final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); + + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId); return; @@ -859,7 +947,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) { - for (ServiceProvider provider : mRegistry.getProviders()) { + + for (ServiceProvider provider : mServiceProviders) { provider.setUdfpsOverlayController(controller); } } @@ -867,15 +956,22 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setSidefpsController(@NonNull ISidefpsController controller) { - for (ServiceProvider provider : mRegistry.getProviders()) { + + for (ServiceProvider provider : mServiceProviders) { provider.setSidefpsController(controller); } } - @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + @Override + public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + FingerprintService.this.registerBiometricStateListener(listener); + } + @Override public void onPowerPressed() { - for (ServiceProvider provider : mRegistry.getProviders()) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + for (ServiceProvider provider : mServiceProviders) { provider.onPowerPressed(); } } @@ -885,7 +981,6 @@ public class FingerprintService extends SystemService { this(context, BiometricContext.getInstance(context), () -> IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), - () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR), (fqName) -> IFingerprint.Stub.asInterface( Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)))); } @@ -893,34 +988,61 @@ public class FingerprintService extends SystemService { @VisibleForTesting FingerprintService(Context context, BiometricContext biometricContext, - Supplier<IBiometricService> biometricServiceSupplier, - Supplier<String[]> aidlInstanceNameSupplier, + Supplier<IBiometricService> biometricServiceProvider, Function<String, IFingerprint> fingerprintProvider) { super(context); mBiometricContext = biometricContext; - mAidlInstanceNameSupplier = aidlInstanceNameSupplier; + mBiometricServiceSupplier = biometricServiceProvider; mIFingerprintProvider = fingerprintProvider; mAppOps = context.getSystemService(AppOpsManager.class); mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher(); mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); - mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); + mServiceProviders = new ArrayList<>(); + mBiometricStateCallback = new BiometricStateCallback(); + mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>(); + mSensorProps = new ArrayList<>(); mHandler = new Handler(Looper.getMainLooper()); - mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier); - mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FingerprintSensorPropertiesInternal> sensors) { - mBiometricStateCallback.start(mRegistry.getProviders()); - } - }); } - @NonNull - private List<ServiceProvider> getHidlProviders( + @VisibleForTesting + void registerAuthenticatorsForService(@NonNull List<String> aidlInstanceNames, @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { - final List<ServiceProvider> providers = new ArrayList<>(); + addHidlProviders(hidlSensors); + addAidlProviders(Utils.filterAvailableHalInstances(getContext(), aidlInstanceNames)); + + final IBiometricService biometricService = mBiometricServiceSupplier.get(); + + // Register each sensor individually with BiometricService + for (ServiceProvider provider : mServiceProviders) { + final List<FingerprintSensorPropertiesInternal> props = + provider.getSensorProperties(); + for (FingerprintSensorPropertiesInternal prop : props) { + final int sensorId = prop.sensorId; + @BiometricManager.Authenticators.Types final int strength = + Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength); + final FingerprintAuthenticator authenticator = new FingerprintAuthenticator( + mServiceWrapper, sensorId); + try { + biometricService.registerAuthenticator(sensorId, TYPE_FINGERPRINT, + strength, authenticator); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId); + } + } + } + + synchronized (mLock) { + for (ServiceProvider provider : mServiceProviders) { + mSensorProps.addAll(provider.getSensorProperties()); + } + } + broadcastCurrentEnrollmentState(null); // broadcasts to all listeners + broadcastAllAuthenticatorsRegistered(); + } + + private void addHidlProviders(List<FingerprintSensorPropertiesInternal> hidlSensors) { for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) { final Fingerprint21 fingerprint21; if ((Build.IS_USERDEBUG || Build.IS_ENG) @@ -937,16 +1059,11 @@ public class FingerprintService extends SystemService { mBiometricStateCallback, hidlSensor, mHandler, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } - providers.add(fingerprint21); + mServiceProviders.add(fingerprint21); } - - return providers; } - @NonNull - private List<ServiceProvider> getAidlProviders(@NonNull List<String> instances) { - final List<ServiceProvider> providers = new ArrayList<>(); - + private void addAidlProviders(List<String> instances) { for (String instance : instances) { final String fqName = IFingerprint.DESCRIPTOR + "/" + instance; final IFingerprint fp = mIFingerprintProvider.apply(fqName); @@ -958,7 +1075,7 @@ public class FingerprintService extends SystemService { mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext); Slog.i(TAG, "Adding AIDL provider: " + fqName); - providers.add(provider); + mServiceProviders.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } @@ -966,8 +1083,38 @@ public class FingerprintService extends SystemService { Slog.e(TAG, "Unable to get declared service: " + fqName); } } + } - return providers; + // Notifies the callbacks that all of the authenticators have been registered and removes the + // invoked callbacks from the callback list. + private void broadcastAllAuthenticatorsRegistered() { + // Make a local copy of the data so it can be used outside of the synchronized block when + // making Binder calls. + final List<IFingerprintAuthenticatorsRegisteredCallback> callbacks = new ArrayList<>(); + final List<FingerprintSensorPropertiesInternal> props; + synchronized (mLock) { + if (!mSensorProps.isEmpty()) { + props = new ArrayList<>(mSensorProps); + } else { + Slog.e(TAG, "mSensorProps is empty"); + return; + } + final int n = mAuthenticatorsRegisteredCallbacks.beginBroadcast(); + for (int i = 0; i < n; ++i) { + final IFingerprintAuthenticatorsRegisteredCallback cb = + mAuthenticatorsRegisteredCallbacks.getBroadcastItem(i); + callbacks.add(cb); + mAuthenticatorsRegisteredCallbacks.unregister(cb); + } + mAuthenticatorsRegisteredCallbacks.finishBroadcast(); + } + for (IFingerprintAuthenticatorsRegisteredCallback cb : callbacks) { + try { + cb.onAllAuthenticatorsRegistered(props); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAllAuthenticatorsRegistered", e); + } + } } @Override @@ -975,9 +1122,51 @@ public class FingerprintService extends SystemService { publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper); } + @Nullable + private ServiceProvider getProviderForSensor(int sensorId) { + for (ServiceProvider provider : mServiceProviders) { + if (provider.containsSensor(sensorId)) { + return provider; + } + } + return null; + } + + /** + * For devices with only a single provider, returns that provider. If multiple providers, + * returns the first one. If no providers, returns null. + */ + @Nullable + private Pair<Integer, ServiceProvider> getSingleProvider() { + final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties(); + if (properties.isEmpty()) { + Slog.e(TAG, "No providers found"); + return null; + } + + // Theoretically we can just return the first provider, but maybe this is easier to + // understand. + final int sensorId = properties.get(0).sensorId; + for (ServiceProvider provider : mServiceProviders) { + if (provider.containsSensor(sensorId)) { + return new Pair<>(sensorId, provider); + } + } + + Slog.e(TAG, "Provider not found"); + return null; + } + + @NonNull + private List<FingerprintSensorPropertiesInternal> getSensorProperties() { + synchronized (mLock) { + return mSensorProps; + } + } + @NonNull private List<Fingerprint> getEnrolledFingerprintsDeprecated(int userId, String opPackageName) { - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for getEnrolledFingerprintsDeprecated, caller: " + opPackageName); @@ -1040,7 +1229,7 @@ public class FingerprintService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { Slog.i(TAG, "Sync virtual enrollments"); final int userId = ActivityManager.getCurrentUser(); - for (ServiceProvider provider : mRegistry.getProviders()) { + for (ServiceProvider provider : mServiceProviders) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */, true /* favorHalEnrollments */); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java deleted file mode 100644 index 33810b764f23..000000000000 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java +++ /dev/null @@ -1,72 +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.biometrics.sensors.fingerprint; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.hardware.biometrics.BiometricManager; -import android.hardware.biometrics.IBiometricService; -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; -import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; -import android.hardware.fingerprint.IFingerprintService; -import android.os.RemoteException; -import android.util.Slog; - -import com.android.server.biometrics.Utils; -import com.android.server.biometrics.sensors.BiometricServiceRegistry; - -import java.util.List; -import java.util.function.Supplier; - -/** Registry for {@link IFingerprintService} providers. */ -public class FingerprintServiceRegistry extends BiometricServiceRegistry<ServiceProvider, - FingerprintSensorPropertiesInternal, IFingerprintAuthenticatorsRegisteredCallback> { - - private static final String TAG = "FingerprintServiceRegistry"; - - @NonNull - private final IFingerprintService mService; - - /** Creates a new registry tied to the given service. */ - public FingerprintServiceRegistry(@NonNull IFingerprintService service, - @Nullable Supplier<IBiometricService> biometricSupplier) { - super(biometricSupplier); - mService = service; - } - - @Override - protected void registerService(@NonNull IBiometricService service, - @NonNull FingerprintSensorPropertiesInternal props) { - @BiometricManager.Authenticators.Types final int strength = - Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength); - try { - service.registerAuthenticator(props.sensorId, TYPE_FINGERPRINT, strength, - new FingerprintAuthenticator(mService, props.sensorId)); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId); - } - } - - @Override - protected void invokeRegisteredCallback( - @NonNull IFingerprintAuthenticatorsRegisteredCallback callback, - @NonNull List<FingerprintSensorPropertiesInternal> allProps) throws RemoteException { - callback.onAllAuthenticatorsRegistered(allProps); - } -} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 9075e7ec2080..275d7e445a75 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -28,11 +28,14 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; +import android.util.proto.ProtoOutputStream; -import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutTracker; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.List; /** @@ -56,8 +59,23 @@ import java.util.List; * fail safely. */ @SuppressWarnings("deprecation") -public interface ServiceProvider extends - BiometricServiceProvider<FingerprintSensorPropertiesInternal> { +public interface ServiceProvider { + /** + * Checks if the specified sensor is owned by this provider. + */ + boolean containsSensor(int sensorId); + + @NonNull + List<FingerprintSensorPropertiesInternal> getSensorProperties(); + + /** + * Returns the internal properties of the specified sensor, if owned by this provider. + * + * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor. + * @return An object representing the internal properties of the specified sensor. + */ + @Nullable + FingerprintSensorPropertiesInternal getSensorProperties(int sensorId); void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken); @@ -108,11 +126,16 @@ public interface ServiceProvider extends void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments); + boolean isHardwareDetected(int sensorId); + void rename(int sensorId, int fingerId, int userId, @NonNull String name); @NonNull List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId); + @LockoutTracker.LockoutMode + int getLockoutModeForUser(int sensorId, int userId); + /** * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to * be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} @@ -120,6 +143,7 @@ public interface ServiceProvider extends void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback); + long getAuthenticatorId(int sensorId, int userId); void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major); @@ -137,6 +161,13 @@ public interface ServiceProvider extends */ void setSidefpsController(@NonNull ISidefpsController controller); + void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, + boolean clearSchedulerBuffer); + + void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); + + void dumpInternal(int sensorId, @NonNull PrintWriter pw); + @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName); 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 3fe6332fcaa0..2dc005206b42 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 @@ -565,11 +565,6 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override - public boolean hasEnrollments(int sensorId, int userId) { - return !getEnrolledFingerprints(sensorId, userId).isEmpty(); - } - - @Override public void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback) { mHandler.post(() -> { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 0e6df8e0df77..ed482f013869 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -789,11 +789,6 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public boolean hasEnrollments(int sensorId, int userId) { - return !getEnrolledFingerprints(sensorId, userId).isEmpty(); - } - - @Override @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) { return mLockoutTracker.getLockoutModeForUser(userId); } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 05d32d1d9023..6145a91cf7cd 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -1008,8 +1008,9 @@ class AutomaticBrightnessController { final String packageName = info.topActivity.getPackageName(); // If the app didn't change, there's nothing to do. Otherwise, we have to // update the category and re-apply the brightness correction. - if (mForegroundAppPackageName != null - && mForegroundAppPackageName.equals(packageName)) { + String currentForegroundAppPackageName = mForegroundAppPackageName; + if (currentForegroundAppPackageName != null + && currentForegroundAppPackageName.equals(packageName)) { return; } mPendingForegroundAppPackageName = packageName; diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 7b60345caf87..4e0489a5c4bc 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -218,6 +218,7 @@ public final class DreamManagerService extends SystemService { }, pw, "", 200); } + /** Whether a real dream is occurring. */ private boolean isDreamingInternal() { synchronized (mLock) { return mCurrentDreamToken != null && !mCurrentDreamIsPreview @@ -225,6 +226,13 @@ public final class DreamManagerService extends SystemService { } } + /** Whether a real dream, or a dream preview is occurring. */ + private boolean isDreamingOrInPreviewInternal() { + synchronized (mLock) { + return mCurrentDreamToken != null && !mCurrentDreamIsWaking; + } + } + protected void requestStartDreamFromShell() { requestDreamInternal(); } @@ -695,6 +703,19 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call + public boolean isDreamingOrInPreview() { + checkPermission(android.Manifest.permission.READ_DREAM_STATE); + + final long ident = Binder.clearCallingIdentity(); + try { + return isDreamingOrInPreviewInternal(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + + @Override // Binder call public void dream() { checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2070c2b31236..e03b423bf83e 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5170,7 +5170,8 @@ public class NotificationManagerService extends SystemService { extras, mRankingHelper.findExtractor(ValidateNotificationPeople.class), MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS, - MATCHES_CALL_FILTER_TIMEOUT_AFFINITY); + MATCHES_CALL_FILTER_TIMEOUT_AFFINITY, + Binder.getCallingUid()); } @Override diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java index 7d7f3a96595c..c0bc474cd8fa 100644 --- a/services/core/java/com/android/server/notification/ZenLog.java +++ b/services/core/java/com/android/server/notification/ZenLog.java @@ -66,6 +66,8 @@ public class ZenLog { private static final int TYPE_SET_NOTIFICATION_POLICY = 16; private static final int TYPE_SET_CONSOLIDATED_ZEN_POLICY = 17; private static final int TYPE_MATCHES_CALL_FILTER = 18; + private static final int TYPE_RECORD_CALLER = 19; + private static final int TYPE_CHECK_REPEAT_CALLER = 20; private static int sNext; private static int sSize; @@ -167,11 +169,28 @@ public class ZenLog { + hintsToString(newHints) + ",listeners=" + listenerCount); } - /* + /** * Trace calls to matchesCallFilter with the result of the call and the reason for the result. */ - public static void traceMatchesCallFilter(boolean result, String reason) { - append(TYPE_MATCHES_CALL_FILTER, "result=" + result + ", reason=" + reason); + public static void traceMatchesCallFilter(boolean result, String reason, int callingUid) { + append(TYPE_MATCHES_CALL_FILTER, "result=" + result + ", reason=" + reason + + ", calling uid=" + callingUid); + } + + /** + * Trace what information is available about an incoming call when it's recorded + */ + public static void traceRecordCaller(boolean hasPhone, boolean hasUri) { + append(TYPE_RECORD_CALLER, "has phone number=" + hasPhone + ", has uri=" + hasUri); + } + + /** + * Trace what information was provided about a caller when checking whether it is from a repeat + * caller + */ + public static void traceCheckRepeatCaller(boolean found, boolean hasPhone, boolean hasUri) { + append(TYPE_CHECK_REPEAT_CALLER, "res=" + found + ", given phone number=" + hasPhone + + ", given uri=" + hasUri); } private static String subscribeResult(IConditionProvider provider, RemoteException e) { @@ -198,6 +217,8 @@ public class ZenLog { case TYPE_SET_NOTIFICATION_POLICY: return "set_notification_policy"; case TYPE_SET_CONSOLIDATED_ZEN_POLICY: return "set_consolidated_policy"; case TYPE_MATCHES_CALL_FILTER: return "matches_call_filter"; + case TYPE_RECORD_CALLER: return "record_caller"; + case TYPE_CHECK_REPEAT_CALLER: return "check_repeat_caller"; default: return "unknown"; } } diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java index b0d40efed690..7e36aed81d4a 100644 --- a/services/core/java/com/android/server/notification/ZenModeFiltering.java +++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java @@ -101,23 +101,24 @@ public class ZenModeFiltering { */ public static boolean matchesCallFilter(Context context, int zen, NotificationManager.Policy consolidatedPolicy, UserHandle userHandle, Bundle extras, - ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) { + ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity, + int callingUid) { if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) { - ZenLog.traceMatchesCallFilter(false, "no interruptions"); + ZenLog.traceMatchesCallFilter(false, "no interruptions", callingUid); return false; // nothing gets through } if (zen == Global.ZEN_MODE_ALARMS) { - ZenLog.traceMatchesCallFilter(false, "alarms only"); + ZenLog.traceMatchesCallFilter(false, "alarms only", callingUid); return false; // not an alarm } if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { if (consolidatedPolicy.allowRepeatCallers() && REPEAT_CALLERS.isRepeat(context, extras, null)) { - ZenLog.traceMatchesCallFilter(true, "repeat caller"); + ZenLog.traceMatchesCallFilter(true, "repeat caller", callingUid); return true; } if (!consolidatedPolicy.allowCalls()) { - ZenLog.traceMatchesCallFilter(false, "calls not allowed"); + ZenLog.traceMatchesCallFilter(false, "calls not allowed", callingUid); return false; // no other calls get through } if (validator != null) { @@ -125,11 +126,12 @@ public class ZenModeFiltering { contactsTimeoutMs, timeoutAffinity); boolean match = audienceMatches(consolidatedPolicy.allowCallsFrom(), contactAffinity); - ZenLog.traceMatchesCallFilter(match, "contact affinity " + contactAffinity); + ZenLog.traceMatchesCallFilter(match, "contact affinity " + contactAffinity, + callingUid); return match; } } - ZenLog.traceMatchesCallFilter(true, "no restrictions"); + ZenLog.traceMatchesCallFilter(true, "no restrictions", callingUid); return true; } @@ -419,6 +421,7 @@ public class ZenModeFiltering { private synchronized void recordCallers(String[] people, ArraySet<String> phoneNumbers, long now) { + boolean recorded = false, hasTel = false, hasOther = false; for (int i = 0; i < people.length; i++) { String person = people[i]; if (person == null) continue; @@ -427,10 +430,16 @@ public class ZenModeFiltering { // while ideally we should not need to decode this, sometimes we have seen tel // numbers given in an encoded format String tel = Uri.decode(uri.getSchemeSpecificPart()); - if (tel != null) mTelCalls.put(tel, now); + if (tel != null) { + mTelCalls.put(tel, now); + recorded = true; + hasTel = true; + } } else { // for non-tel calls, store the entire string, uri-component and all mOtherCalls.put(person, now); + recorded = true; + hasOther = true; } } @@ -438,9 +447,16 @@ public class ZenModeFiltering { // provided; these are in the format of just a phone number string if (phoneNumbers != null) { for (String num : phoneNumbers) { - if (num != null) mTelCalls.put(num, now); + if (num != null) { + mTelCalls.put(num, now); + recorded = true; + hasTel = true; + } } } + if (recorded) { + ZenLog.traceRecordCaller(hasTel, hasOther); + } } // helper function to check mTelCalls array for a number, and also check its decoded @@ -468,6 +484,8 @@ public class ZenModeFiltering { // previously recorded phone call. private synchronized boolean checkCallers(Context context, String[] people, ArraySet<String> phoneNumbers) { + boolean found = false, checkedTel = false, checkedOther = false; + // get the default country code for checking telephone numbers final String defaultCountryCode = context.getSystemService(TelephonyManager.class).getNetworkCountryIso(); @@ -477,12 +495,14 @@ public class ZenModeFiltering { final Uri uri = Uri.parse(person); if ("tel".equals(uri.getScheme())) { String number = uri.getSchemeSpecificPart(); + checkedTel = true; if (checkForNumber(number, defaultCountryCode)) { - return true; + found = true; } } else { + checkedOther = true; if (mOtherCalls.containsKey(person)) { - return true; + found = true; } } } @@ -490,14 +510,16 @@ public class ZenModeFiltering { // also check any passed-in phone numbers if (phoneNumbers != null) { for (String num : phoneNumbers) { + checkedTel = true; if (checkForNumber(num, defaultCountryCode)) { - return true; + found = true; } } } // no matches - return false; + ZenLog.traceCheckRepeatCaller(found, checkedTel, checkedOther); + return found; } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 6135fe8acbed..d42667951608 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -177,10 +177,12 @@ public class ZenModeHelper { } public boolean matchesCallFilter(UserHandle userHandle, Bundle extras, - ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) { + ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity, + int callingUid) { synchronized (mConfig) { return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy, - userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity); + userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity, + callingUid); } } diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index b05e44f9e625..da76738fa025 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -306,7 +306,7 @@ final class OverlayManagerShellCommand extends ShellCommand { } else { final String resourceName = getNextArgRequired(); final String typeStr = getNextArgRequired(); - final String strData = getNextArgRequired(); + final String strData = String.join(" ", peekRemainingArgs()); addOverlayValue(overlayBuilder, resourceName, typeStr, strData); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e9637ec30302..99c95d56083a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7062,7 +7062,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mResolveActivity.processName = pkg.getProcessName(); mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS - | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS; + | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; mResolveActivity.theme = 0; mResolveActivity.exported = true; mResolveActivity.enabled = true; @@ -7095,7 +7096,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS - | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; + | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert; mResolveActivity.exported = true; mResolveActivity.enabled = true; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c64e5255c78c..d11adc13795d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5640,7 +5640,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppResumed: wasStopped=%b %s", wasStopped, this); mAppStopped = false; - // Allow the window to turn the screen on once the app is resumed again. + // Allow the window to turn the screen on once the app is started and resumed. if (mAtmService.getActivityStartController().isInExecution()) { setCurrentLaunchCanTurnScreenOn(true); } diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 77d6097a9c69..8f18064b4723 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -94,6 +94,7 @@ public class ActivityStartController { boolean mCheckedForSetup = false; + /** Whether an {@link ActivityStarter} is currently executing (starting an Activity). */ private boolean mInExecution = false; /** @@ -129,7 +130,7 @@ public class ActivityStartController { return mFactory.obtain().setIntent(intent).setReason(reason); } - void onExecutionStarted(ActivityStarter starter) { + void onExecutionStarted() { mInExecution = true; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index abedd96d1cde..619d693068d4 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1267,7 +1267,7 @@ class ActivityStarter { } private void onExecutionStarted() { - mController.onExecutionStarted(this); + mController.onExecutionStarted(); } private boolean isHomeApp(int uid, @Nullable String packageName) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cae722ece7c7..e23380088dc4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2623,27 +2623,32 @@ public class WindowManagerService extends IWindowManager.Stub transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - String reason = null; - if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) { - reason = "applyAnimation"; - focusMayChange = true; - win.mAnimatingExit = true; - } else if (win.mDisplayContent.okToAnimate() && win.isExitAnimationRunningSelfOrParent()) { - // Currently in a hide animation... turn this into - // an exit. - win.mAnimatingExit = true; - } else if (win.mDisplayContent.okToAnimate() - && win.mDisplayContent.mWallpaperController.isWallpaperTarget(win) - && win.mAttrs.type != TYPE_NOTIFICATION_SHADE) { - reason = "isWallpaperTarget"; - // If the wallpaper is currently behind this app window, we need to change both of them - // inside of a transaction to avoid artifacts. - // For NotificationShade, sysui is in charge of running window animation and it updates - // the client view visibility only after both NotificationShade and the wallpaper are - // hidden. So we don't need to care about exit animation, but can destroy its surface - // immediately. - win.mAnimatingExit = true; - } else { + if (win.isWinVisibleLw() && win.mDisplayContent.okToAnimate()) { + String reason = null; + if (winAnimator.applyAnimationLocked(transit, false)) { + reason = "applyAnimation"; + focusMayChange = true; + win.mAnimatingExit = true; + } else if (win.isExitAnimationRunningSelfOrParent()) { + reason = "animating"; + win.mAnimatingExit = true; + } else if (win.mDisplayContent.mWallpaperController.isWallpaperTarget(win) + && win.mAttrs.type != TYPE_NOTIFICATION_SHADE) { + reason = "isWallpaperTarget"; + // If the wallpaper is currently behind this app window, they should be updated + // in a transaction to avoid artifacts. + // For NotificationShade, sysui is in charge of running window animation and it + // updates the client view visibility only after both NotificationShade and the + // wallpaper are hidden. So the exit animation is not needed and can destroy its + // surface immediately. + win.mAnimatingExit = true; + } + if (reason != null) { + ProtoLog.d(WM_DEBUG_ANIM, + "Set animatingExit: reason=startExitingAnimation/%s win=%s", reason, win); + } + } + if (!win.mAnimatingExit) { boolean stopped = win.mActivityRecord == null || win.mActivityRecord.mAppStopped; // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces // will later actually destroy the surface if we do not do so here. Normally we leave @@ -2651,10 +2656,6 @@ public class WindowManagerService extends IWindowManager.Stub win.mDestroying = true; win.destroySurface(false, stopped); } - if (reason != null) { - ProtoLog.d(WM_DEBUG_ANIM, "Set animatingExit: reason=startExitingAnimation/%s win=%s", - reason, win); - } if (mAccessibilityController.hasCallbacks()) { mAccessibilityController.onWindowTransition(win, transit); } diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java index 16c4c29a798e..6ead44a8cd1b 100644 --- a/services/people/java/com/android/server/people/data/ConversationInfo.java +++ b/services/people/java/com/android/server/people/data/ConversationInfo.java @@ -26,6 +26,7 @@ import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo.ShortcutFlags; import android.net.Uri; import android.text.TextUtils; +import android.util.Log; import android.util.Slog; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; @@ -37,6 +38,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.EOFException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -50,6 +52,11 @@ import java.util.Objects; * Represents a conversation that is provided by the app based on {@link ShortcutInfo}. */ public class ConversationInfo { + private static final boolean DEBUG = false; + + // Schema version for the backup payload. Must be incremented whenever fields are added in + // backup payload. + private static final int VERSION = 1; private static final String TAG = ConversationInfo.class.getSimpleName(); @@ -100,6 +107,8 @@ public class ConversationInfo { private long mLastEventTimestamp; + private long mCreationTimestamp; + @ShortcutFlags private int mShortcutFlags; @@ -116,6 +125,7 @@ public class ConversationInfo { mNotificationChannelId = builder.mNotificationChannelId; mParentNotificationChannelId = builder.mParentNotificationChannelId; mLastEventTimestamp = builder.mLastEventTimestamp; + mCreationTimestamp = builder.mCreationTimestamp; mShortcutFlags = builder.mShortcutFlags; mConversationFlags = builder.mConversationFlags; mCurrStatuses = builder.mCurrStatuses; @@ -170,6 +180,13 @@ public class ConversationInfo { return mLastEventTimestamp; } + /** + * Timestamp of the creation of the conversationInfo. + */ + long getCreationTimestamp() { + return mCreationTimestamp; + } + /** Whether the shortcut for this conversation is set long-lived by the app. */ public boolean isShortcutLongLived() { return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED); @@ -241,6 +258,7 @@ public class ConversationInfo { && Objects.equals(mNotificationChannelId, other.mNotificationChannelId) && Objects.equals(mParentNotificationChannelId, other.mParentNotificationChannelId) && Objects.equals(mLastEventTimestamp, other.mLastEventTimestamp) + && mCreationTimestamp == other.mCreationTimestamp && mShortcutFlags == other.mShortcutFlags && mConversationFlags == other.mConversationFlags && Objects.equals(mCurrStatuses, other.mCurrStatuses); @@ -250,7 +268,7 @@ public class ConversationInfo { public int hashCode() { return Objects.hash(mShortcutId, mLocusId, mContactUri, mContactPhoneNumber, mNotificationChannelId, mParentNotificationChannelId, mLastEventTimestamp, - mShortcutFlags, mConversationFlags, mCurrStatuses); + mCreationTimestamp, mShortcutFlags, mConversationFlags, mCurrStatuses); } @Override @@ -264,6 +282,7 @@ public class ConversationInfo { sb.append(", notificationChannelId=").append(mNotificationChannelId); sb.append(", parentNotificationChannelId=").append(mParentNotificationChannelId); sb.append(", lastEventTimestamp=").append(mLastEventTimestamp); + sb.append(", creationTimestamp=").append(mCreationTimestamp); sb.append(", statuses=").append(mCurrStatuses); sb.append(", shortcutFlags=0x").append(Integer.toHexString(mShortcutFlags)); sb.append(" ["); @@ -329,6 +348,7 @@ public class ConversationInfo { mParentNotificationChannelId); } protoOutputStream.write(ConversationInfoProto.LAST_EVENT_TIMESTAMP, mLastEventTimestamp); + protoOutputStream.write(ConversationInfoProto.CREATION_TIMESTAMP, mCreationTimestamp); protoOutputStream.write(ConversationInfoProto.SHORTCUT_FLAGS, mShortcutFlags); protoOutputStream.write(ConversationInfoProto.CONVERSATION_FLAGS, mConversationFlags); if (mContactPhoneNumber != null) { @@ -352,6 +372,8 @@ public class ConversationInfo { out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : ""); out.writeUTF(mParentNotificationChannelId != null ? mParentNotificationChannelId : ""); out.writeLong(mLastEventTimestamp); + out.writeInt(VERSION); + out.writeLong(mCreationTimestamp); // ConversationStatus is a transient object and not persisted } catch (IOException e) { Slog.e(TAG, "Failed to write fields to backup payload.", e); @@ -399,6 +421,9 @@ public class ConversationInfo { builder.setLastEventTimestamp(protoInputStream.readLong( ConversationInfoProto.LAST_EVENT_TIMESTAMP)); break; + case (int) ConversationInfoProto.CREATION_TIMESTAMP: + builder.setCreationTimestamp(protoInputStream.readLong( + ConversationInfoProto.CREATION_TIMESTAMP)); case (int) ConversationInfoProto.SHORTCUT_FLAGS: builder.setShortcutFlags(protoInputStream.readInt( ConversationInfoProto.SHORTCUT_FLAGS)); @@ -448,6 +473,10 @@ public class ConversationInfo { builder.setParentNotificationChannelId(parentNotificationChannelId); } builder.setLastEventTimestamp(in.readLong()); + int payloadVersion = maybeReadVersion(in); + if (payloadVersion == 1) { + builder.setCreationTimestamp(in.readLong()); + } } catch (IOException e) { Slog.e(TAG, "Failed to read conversation info fields from backup payload.", e); return null; @@ -455,6 +484,16 @@ public class ConversationInfo { return builder.build(); } + private static int maybeReadVersion(DataInputStream in) throws IOException { + try { + return in.readInt(); + } catch (EOFException eofException) { + // EOF is expected if using old backup payload protocol. + if (DEBUG) Log.d(TAG, "Eof reached for data stream, missing version number"); + return 0; + } + } + /** * Builder class for {@link ConversationInfo} objects. */ @@ -479,6 +518,8 @@ public class ConversationInfo { private long mLastEventTimestamp; + private long mCreationTimestamp; + @ShortcutFlags private int mShortcutFlags; @@ -502,6 +543,7 @@ public class ConversationInfo { mNotificationChannelId = conversationInfo.mNotificationChannelId; mParentNotificationChannelId = conversationInfo.mParentNotificationChannelId; mLastEventTimestamp = conversationInfo.mLastEventTimestamp; + mCreationTimestamp = conversationInfo.mCreationTimestamp; mShortcutFlags = conversationInfo.mShortcutFlags; mConversationFlags = conversationInfo.mConversationFlags; mCurrStatuses = conversationInfo.mCurrStatuses; @@ -542,6 +584,11 @@ public class ConversationInfo { return this; } + Builder setCreationTimestamp(long creationTimestamp) { + mCreationTimestamp = creationTimestamp; + return this; + } + Builder setShortcutFlags(@ShortcutFlags int shortcutFlags) { mShortcutFlags = shortcutFlags; return this; diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index d305fc5d7dc4..693f3a0cf8a0 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -816,10 +816,18 @@ public class DataManager { } private boolean isCachedRecentConversation(ConversationInfo conversationInfo) { + return isEligibleForCleanUp(conversationInfo) + && conversationInfo.getLastEventTimestamp() > 0L; + } + + /** + * Conversations that are cached and not customized are eligible for clean-up, even if they + * don't have an associated notification event with them. + */ + private boolean isEligibleForCleanUp(ConversationInfo conversationInfo) { return conversationInfo.isShortcutCachedForNotification() && Objects.equals(conversationInfo.getNotificationChannelId(), - conversationInfo.getParentNotificationChannelId()) - && conversationInfo.getLastEventTimestamp() > 0L; + conversationInfo.getParentNotificationChannelId()); } private boolean hasActiveNotifications(String packageName, @UserIdInt int userId, @@ -842,14 +850,14 @@ public class DataManager { } // pair of <package name, conversation info> List<Pair<String, ConversationInfo>> cachedConvos = new ArrayList<>(); - userData.forAllPackages(packageData -> + userData.forAllPackages(packageData -> { packageData.forAllConversations(conversationInfo -> { - if (isCachedRecentConversation(conversationInfo)) { + if (isEligibleForCleanUp(conversationInfo)) { cachedConvos.add( Pair.create(packageData.getPackageName(), conversationInfo)); } - }) - ); + }); + }); if (cachedConvos.size() <= targetCachedCount) { return; } @@ -858,7 +866,9 @@ public class DataManager { PriorityQueue<Pair<String, ConversationInfo>> maxHeap = new PriorityQueue<>( numToUncache + 1, Comparator.comparingLong((Pair<String, ConversationInfo> pair) -> - pair.second.getLastEventTimestamp()).reversed()); + Math.max( + pair.second.getLastEventTimestamp(), + pair.second.getCreationTimestamp())).reversed()); for (Pair<String, ConversationInfo> cached : cachedConvos) { if (hasActiveNotifications(cached.first, userId, cached.second.getShortcutId())) { continue; @@ -893,7 +903,7 @@ public class DataManager { } ConversationInfo.Builder builder = oldConversationInfo != null ? new ConversationInfo.Builder(oldConversationInfo) - : new ConversationInfo.Builder(); + : new ConversationInfo.Builder().setCreationTimestamp(System.currentTimeMillis()); builder.setShortcutId(shortcutInfo.getId()); builder.setLocusId(shortcutInfo.getLocusId()); @@ -1326,7 +1336,8 @@ public class DataManager { } } - private void updateConversationStoreThenNotifyListeners(ConversationStore cs, + @VisibleForTesting + void updateConversationStoreThenNotifyListeners(ConversationStore cs, ConversationInfo modifiedConv, String packageName, int userId) { cs.addOrUpdate(modifiedConv); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java deleted file mode 100644 index 8cd58abcdf0f..000000000000 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java +++ /dev/null @@ -1,165 +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.biometrics.sensors.face; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; -import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; -import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; -import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; -import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.hardware.biometrics.IBiometricService; -import android.hardware.face.FaceSensorProperties; -import android.hardware.face.FaceSensorPropertiesInternal; -import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; -import android.hardware.face.IFaceService; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.ArrayList; -import java.util.List; - -@Presubmit -@SmallTest -public class FaceServiceRegistryTest { - - private static final int SENSOR_ID_1 = 1; - private static final int SENSOR_ID_2 = 2; - - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock - private IBiometricService mBiometricService; - @Mock - private IFaceService mFaceService; - @Mock - private ServiceProvider mProvider1; - @Mock - private ServiceProvider mProvider2; - @Captor - private ArgumentCaptor<Integer> mIdCaptor; - @Captor - private ArgumentCaptor<Integer> mStrengthCaptor; - - private FaceSensorPropertiesInternal mProvider1Props; - private FaceSensorPropertiesInternal mProvider2Props; - private FaceServiceRegistry mRegistry; - - @Before - public void setup() { - mProvider1Props = new FaceSensorPropertiesInternal(SENSOR_ID_1, - STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */, - List.of(), FaceSensorProperties.TYPE_RGB, - true /* supportsFace Detection */, - true /* supportsSelfIllumination */, - false /* resetLockoutRequiresHardwareAuthToken */); - mProvider2Props = new FaceSensorPropertiesInternal(SENSOR_ID_2, - STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, - List.of(), FaceSensorProperties.TYPE_IR, - true /* supportsFace Detection */, - true /* supportsSelfIllumination */, - false /* resetLockoutRequiresHardwareAuthToken */); - - when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props)); - when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true); - when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props)); - when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true); - mRegistry = new FaceServiceRegistry(mFaceService, () -> mBiometricService); - } - - @Test - public void registersAllProviders() throws Exception { - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2); - assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props); - verify(mBiometricService, times(2)).registerAuthenticator( - mIdCaptor.capture(), eq(TYPE_FACE), mStrengthCaptor.capture(), any()); - assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2); - assertThat(mStrengthCaptor.getAllValues()) - .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG); - } - - @Test - public void getsProviderById() { - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); - assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2); - assertThat(mRegistry.getProviderForSensor(500)).isNull(); - } - - @Test - public void getsSingleProvider() { - mRegistry.registerAllInBackground(() -> List.of(mProvider1)); - - assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); - assertThat(mRegistry.getProviders()).containsExactly(mProvider1); - assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); - } - - @Test - public void registersListenerBeforeAllRegistered() { - final List<FaceSensorPropertiesInternal> all = new ArrayList<>(); - mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FaceSensorPropertiesInternal> sensors) { - all.addAll(sensors); - } - }); - - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - assertThat(all).containsExactly(mProvider1Props, mProvider2Props); - } - - @Test - public void registersListenerAfterAllRegistered() { - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - final List<FaceSensorPropertiesInternal> all = new ArrayList<>(); - mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FaceSensorPropertiesInternal> sensors) { - all.addAll(sensors); - } - }); - - assertThat(all).containsExactly(mProvider1Props, mProvider2Props); - } -} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java index 0e30782eaece..5f88c99b1d1e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java @@ -16,8 +16,6 @@ package com.android.server.biometrics.sensors.fingerprint; -import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; - import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -26,70 +24,35 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricStateListener; -import android.hardware.biometrics.SensorPropertiesInternal; -import android.os.UserManager; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import com.android.server.biometrics.sensors.AuthenticationClient; -import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.EnrollClient; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.List; +import org.mockito.MockitoAnnotations; @Presubmit @SmallTest public class BiometricStateCallbackTest { - private static final int USER_ID = 10; - private static final int SENSOR_ID = 2; - - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - private BiometricStateCallback<FakeProvider, SensorPropertiesInternal> mCallback; + private BiometricStateCallback mCallback; @Mock - private UserManager mUserManager; - @Mock - private BiometricStateListener mBiometricStateListener; - @Mock - private FakeProvider mFakeProvider; - - private SensorPropertiesInternal mFakeProviderProps; + BiometricStateListener mBiometricStateListener; @Before public void setup() { - mFakeProviderProps = new SensorPropertiesInternal(SENSOR_ID, STRENGTH_STRONG, - 5 /* maxEnrollmentsPerUser */, List.of(), - false /* resetLockoutRequiresHardwareAuthToken */, - false /* resetLockoutRequiresChallenge */); - when(mFakeProvider.getSensorProperties()).thenReturn(List.of(mFakeProviderProps)); - when(mFakeProvider.getSensorProperties(eq(SENSOR_ID))).thenReturn(mFakeProviderProps); - when(mFakeProvider.hasEnrollments(eq(SENSOR_ID), eq(USER_ID))).thenReturn(true); - when(mUserManager.getAliveUsers()).thenReturn( - List.of(new UserInfo(USER_ID, "name", 0))); - - mCallback = new BiometricStateCallback<>(mUserManager); - mCallback.registerBiometricStateListener(mBiometricStateListener); - } - - @Test - public void startNotifiesEnrollments() { - mCallback.start(List.of(mFakeProvider)); + MockitoAnnotations.initMocks(this); - verify(mBiometricStateListener).onEnrollmentsChanged(eq(USER_ID), eq(SENSOR_ID), eq(true)); + mCallback = new BiometricStateCallback(); + mCallback.registerBiometricStateListener(mBiometricStateListener); } @Test @@ -139,6 +102,4 @@ public class BiometricStateCallbackTest { verify(mBiometricStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(), anyBoolean()); } - - private interface FakeProvider extends BiometricServiceProvider<SensorPropertiesInternal> {} } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java deleted file mode 100644 index 67d94a8f02d8..000000000000 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java +++ /dev/null @@ -1,161 +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.biometrics.sensors.fingerprint; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; -import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; -import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; -import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; -import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.hardware.biometrics.IBiometricService; -import android.hardware.fingerprint.FingerprintSensorProperties; -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; -import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; -import android.hardware.fingerprint.IFingerprintService; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.ArrayList; -import java.util.List; - -@Presubmit -@SmallTest -public class FingerprintServiceRegistryTest { - - private static final int SENSOR_ID_1 = 1; - private static final int SENSOR_ID_2 = 2; - - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock - private IBiometricService mBiometricService; - @Mock - private IFingerprintService mFingerprintService; - @Mock - private ServiceProvider mProvider1; - @Mock - private ServiceProvider mProvider2; - @Captor - private ArgumentCaptor<Integer> mIdCaptor; - @Captor - private ArgumentCaptor<Integer> mStrengthCaptor; - - private FingerprintSensorPropertiesInternal mProvider1Props; - private FingerprintSensorPropertiesInternal mProvider2Props; - private FingerprintServiceRegistry mRegistry; - - @Before - public void setup() { - mProvider1Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_1, - STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */, - List.of(), FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - false /* resetLockoutRequiresHardwareAuthToken */); - mProvider2Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_2, - STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, - List.of(), FingerprintSensorProperties.TYPE_UNKNOWN, - false /* resetLockoutRequiresHardwareAuthToken */); - - when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props)); - when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true); - when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props)); - when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true); - mRegistry = new FingerprintServiceRegistry(mFingerprintService, () -> mBiometricService); - } - - @Test - public void registersAllProviders() throws Exception { - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2); - assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props); - verify(mBiometricService, times(2)).registerAuthenticator( - mIdCaptor.capture(), eq(TYPE_FINGERPRINT), mStrengthCaptor.capture(), any()); - assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2); - assertThat(mStrengthCaptor.getAllValues()) - .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG); - } - - @Test - public void getsProviderById() { - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); - assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2); - assertThat(mRegistry.getProviderForSensor(500)).isNull(); - } - - @Test - public void getsSingleProvider() { - mRegistry.registerAllInBackground(() -> List.of(mProvider1)); - - assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); - assertThat(mRegistry.getProviders()).containsExactly(mProvider1); - assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); - } - - @Test - public void registersListenerBeforeAllRegistered() { - final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>(); - mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FingerprintSensorPropertiesInternal> sensors) { - all.addAll(sensors); - } - }); - - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - assertThat(all).containsExactly(mProvider1Props, mProvider2Props); - } - - @Test - public void registersListenerAfterAllRegistered() { - mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); - - final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>(); - mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FingerprintSensorPropertiesInternal> sensors) { - all.addAll(sensors); - } - }); - - assertThat(all).containsExactly(mProvider1Props, mProvider2Props); - } -} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java index a4048a27dfb5..ca3677e3b5ff 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java @@ -32,8 +32,7 @@ import android.hardware.biometrics.fingerprint.FingerprintSensorType; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorLocation; import android.hardware.biometrics.fingerprint.SensorProps; -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; -import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.testing.TestableContext; @@ -53,8 +52,6 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; @Presubmit @SmallTest @@ -97,12 +94,9 @@ public class FingerprintServiceTest { mContext.getTestablePermissions().setPermission( USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED); - } - private void initServiceWith(String... aidlInstances) { mService = new FingerprintService(mContext, mBiometricContext, () -> mIBiometricService, - () -> aidlInstances, (fqName) -> { if (fqName.endsWith(NAME_DEFAULT)) return mIFingerprintDefault; if (fqName.endsWith(NAME_VIRTUAL)) return mIFingerprintVirtual; @@ -111,50 +105,29 @@ public class FingerprintServiceTest { } @Test - public void registerAuthenticators_defaultOnly() throws Exception { - initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); - - mService.mServiceWrapper.registerAuthenticators(List.of()); - waitForRegistration(); + public void registerAuthenticators_defaultOnly() throws RemoteException { + mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of()); verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any()); } @Test - public void registerAuthenticators_virtualOnly() throws Exception { - initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); + public void registerAuthenticators_virtualOnly() throws RemoteException { Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext), Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1); - mService.mServiceWrapper.registerAuthenticators(List.of()); - waitForRegistration(); + mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of()); verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); } @Test - public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception { - initServiceWith(NAME_VIRTUAL); - - mService.mServiceWrapper.registerAuthenticators(List.of()); - waitForRegistration(); + public void registerAuthenticators_virtualAlwaysWhenNoOther() throws RemoteException { + mService.registerAuthenticatorsForService(List.of(NAME_VIRTUAL), List.of()); verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); } - private void waitForRegistration() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - mService.mServiceWrapper.addAuthenticatorsRegisteredCallback( - new IFingerprintAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FingerprintSensorPropertiesInternal> sensors) { - latch.countDown(); - } - }); - latch.await(5, TimeUnit.SECONDS); - } - private static SensorProps createProps(int id, byte strength, byte type) { final SensorProps props = new SensorProps(); props.commonProps = new CommonProps(); diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java index 8139310a4c3d..c90064eaa810 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java @@ -58,6 +58,7 @@ public final class ConversationInfoTest { .setNotificationChannelId(NOTIFICATION_CHANNEL_ID) .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID) .setLastEventTimestamp(100L) + .setCreationTimestamp(200L) .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED | ShortcutInfo.FLAG_CACHED_NOTIFICATIONS) .setImportant(true) @@ -79,6 +80,7 @@ public final class ConversationInfoTest { assertEquals(PARENT_NOTIFICATION_CHANNEL_ID, conversationInfo.getParentNotificationChannelId()); assertEquals(100L, conversationInfo.getLastEventTimestamp()); + assertEquals(200L, conversationInfo.getCreationTimestamp()); assertTrue(conversationInfo.isShortcutLongLived()); assertTrue(conversationInfo.isShortcutCachedForNotification()); assertTrue(conversationInfo.isImportant()); @@ -105,6 +107,7 @@ public final class ConversationInfoTest { assertNull(conversationInfo.getNotificationChannelId()); assertNull(conversationInfo.getParentNotificationChannelId()); assertEquals(0L, conversationInfo.getLastEventTimestamp()); + assertEquals(0L, conversationInfo.getCreationTimestamp()); assertFalse(conversationInfo.isShortcutLongLived()); assertFalse(conversationInfo.isShortcutCachedForNotification()); assertFalse(conversationInfo.isImportant()); @@ -131,6 +134,7 @@ public final class ConversationInfoTest { .setNotificationChannelId(NOTIFICATION_CHANNEL_ID) .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID) .setLastEventTimestamp(100L) + .setCreationTimestamp(200L) .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED) .setImportant(true) .setNotificationSilenced(true) @@ -154,6 +158,7 @@ public final class ConversationInfoTest { assertEquals(NOTIFICATION_CHANNEL_ID, destination.getNotificationChannelId()); assertEquals(PARENT_NOTIFICATION_CHANNEL_ID, destination.getParentNotificationChannelId()); assertEquals(100L, destination.getLastEventTimestamp()); + assertEquals(200L, destination.getCreationTimestamp()); assertTrue(destination.isShortcutLongLived()); assertFalse(destination.isImportant()); assertTrue(destination.isNotificationSilenced()); @@ -164,4 +169,105 @@ public final class ConversationInfoTest { assertThat(destination.getStatuses()).contains(cs); assertThat(destination.getStatuses()).contains(cs2); } + + @Test + public void testBuildFromAnotherConversation_identicalConversation() { + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build(); + + ConversationInfo source = new ConversationInfo.Builder() + .setShortcutId(SHORTCUT_ID) + .setLocusId(LOCUS_ID) + .setContactUri(CONTACT_URI) + .setContactPhoneNumber(PHONE_NUMBER) + .setNotificationChannelId(NOTIFICATION_CHANNEL_ID) + .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID) + .setLastEventTimestamp(100L) + .setCreationTimestamp(200L) + .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED) + .setImportant(true) + .setNotificationSilenced(true) + .setBubbled(true) + .setPersonImportant(true) + .setPersonBot(true) + .setContactStarred(true) + .addOrUpdateStatus(cs) + .addOrUpdateStatus(cs2) + .build(); + + ConversationInfo destination = new ConversationInfo.Builder(source).build(); + + assertEquals(SHORTCUT_ID, destination.getShortcutId()); + assertEquals(LOCUS_ID, destination.getLocusId()); + assertEquals(CONTACT_URI, destination.getContactUri()); + assertEquals(PHONE_NUMBER, destination.getContactPhoneNumber()); + assertEquals(NOTIFICATION_CHANNEL_ID, destination.getNotificationChannelId()); + assertEquals(PARENT_NOTIFICATION_CHANNEL_ID, destination.getParentNotificationChannelId()); + assertEquals(100L, destination.getLastEventTimestamp()); + assertEquals(200L, destination.getCreationTimestamp()); + assertTrue(destination.isShortcutLongLived()); + assertTrue(destination.isImportant()); + assertTrue(destination.isNotificationSilenced()); + assertTrue(destination.isBubbled()); + assertTrue(destination.isPersonImportant()); + assertTrue(destination.isPersonBot()); + assertTrue(destination.isContactStarred()); + assertThat(destination.getStatuses()).contains(cs); + assertThat(destination.getStatuses()).contains(cs2); + // Also check equals() implementation + assertTrue(source.equals(destination)); + assertTrue(destination.equals(source)); + } + + @Test + public void testBuildFromBackupPayload() { + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build(); + + ConversationInfo conversationInfo = new ConversationInfo.Builder() + .setShortcutId(SHORTCUT_ID) + .setLocusId(LOCUS_ID) + .setContactUri(CONTACT_URI) + .setContactPhoneNumber(PHONE_NUMBER) + .setNotificationChannelId(NOTIFICATION_CHANNEL_ID) + .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID) + .setLastEventTimestamp(100L) + .setCreationTimestamp(200L) + .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED + | ShortcutInfo.FLAG_CACHED_NOTIFICATIONS) + .setImportant(true) + .setNotificationSilenced(true) + .setBubbled(true) + .setDemoted(true) + .setPersonImportant(true) + .setPersonBot(true) + .setContactStarred(true) + .addOrUpdateStatus(cs) + .addOrUpdateStatus(cs2) + .build(); + + ConversationInfo conversationInfoFromBackup = + ConversationInfo.readFromBackupPayload(conversationInfo.getBackupPayload()); + + assertEquals(SHORTCUT_ID, conversationInfoFromBackup.getShortcutId()); + assertEquals(LOCUS_ID, conversationInfoFromBackup.getLocusId()); + assertEquals(CONTACT_URI, conversationInfoFromBackup.getContactUri()); + assertEquals(PHONE_NUMBER, conversationInfoFromBackup.getContactPhoneNumber()); + assertEquals( + NOTIFICATION_CHANNEL_ID, conversationInfoFromBackup.getNotificationChannelId()); + assertEquals(PARENT_NOTIFICATION_CHANNEL_ID, + conversationInfoFromBackup.getParentNotificationChannelId()); + assertEquals(100L, conversationInfoFromBackup.getLastEventTimestamp()); + assertEquals(200L, conversationInfoFromBackup.getCreationTimestamp()); + assertTrue(conversationInfoFromBackup.isShortcutLongLived()); + assertTrue(conversationInfoFromBackup.isShortcutCachedForNotification()); + assertTrue(conversationInfoFromBackup.isImportant()); + assertTrue(conversationInfoFromBackup.isNotificationSilenced()); + assertTrue(conversationInfoFromBackup.isBubbled()); + assertTrue(conversationInfoFromBackup.isDemoted()); + assertTrue(conversationInfoFromBackup.isPersonImportant()); + assertTrue(conversationInfoFromBackup.isPersonBot()); + assertTrue(conversationInfoFromBackup.isContactStarred()); + // ConversationStatus is a transient object and not persisted + } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 2a4896a7b041..66c3f0730404 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -291,7 +291,8 @@ public final class DataManagerTest { mShortcutChangeCallbackCaptor.capture()); mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue(); - verify(mContext, times(2)).registerReceiver(any(), any()); + verify(mContext, times(1)).registerReceiver(any(), any()); + verify(mContext, times(1)).registerReceiver(any(), any(), anyInt()); } @After @@ -1163,6 +1164,76 @@ public final class DataManagerTest { } @Test + public void testUncacheOldestCachedShortcut_missingNotificationEvents() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) { + String shortcutId = TEST_SHORTCUT_ID + i; + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); + mShortcutChangeCallback.onShortcutsAddedOrUpdated( + TEST_PKG_NAME, + Collections.singletonList(shortcut), + UserHandle.of(USER_ID_PRIMARY)); + mLooper.dispatchAll(); + } + + // Only the shortcut #0 is uncached, all the others are not. + verify(mShortcutServiceInternal).uncacheShortcuts( + anyInt(), any(), eq(TEST_PKG_NAME), + eq(Collections.singletonList(TEST_SHORTCUT_ID + 0)), eq(USER_ID_PRIMARY), + eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)); + for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) { + verify(mShortcutServiceInternal, never()).uncacheShortcuts( + anyInt(), anyString(), anyString(), + eq(Collections.singletonList(TEST_SHORTCUT_ID + i)), anyInt(), + eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)); + } + } + + @Test + public void testUncacheOldestCachedShortcut_legacyConversation() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + // Add an extra conversation with a legacy type (no creationTime) + ConversationStore conversationStore = mDataManager + .getUserDataForTesting(USER_ID_PRIMARY) + .getOrCreatePackageData(TEST_PKG_NAME) + .getConversationStore(); + ConversationInfo.Builder builder = new ConversationInfo.Builder(); + builder.setShortcutId(TEST_SHORTCUT_ID + 0); + builder.setShortcutFlags(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); + mDataManager.updateConversationStoreThenNotifyListeners( + conversationStore, + builder.build(), + TEST_PKG_NAME, USER_ID_PRIMARY); + for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) { + String shortcutId = TEST_SHORTCUT_ID + i; + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); + mShortcutChangeCallback.onShortcutsAddedOrUpdated( + TEST_PKG_NAME, + Collections.singletonList(shortcut), + UserHandle.of(USER_ID_PRIMARY)); + mLooper.dispatchAll(); + } + + // Only the shortcut #0 is uncached, all the others are not. + verify(mShortcutServiceInternal).uncacheShortcuts( + anyInt(), any(), eq(TEST_PKG_NAME), + eq(Collections.singletonList(TEST_SHORTCUT_ID + 0)), eq(USER_ID_PRIMARY), + eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)); + for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) { + verify(mShortcutServiceInternal, never()).uncacheShortcuts( + anyInt(), anyString(), anyString(), + eq(Collections.singletonList(TEST_SHORTCUT_ID + i)), anyInt(), + eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)); + } + } + + @Test public void testBackupAndRestoration() throws IntentFilter.MalformedMimeTypeException { mDataManager.onUserUnlocked(USER_ID_PRIMARY); 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 8ac729e29424..c7905a0e8056 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java @@ -8,7 +8,7 @@ * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distriZenbuted on an "AS IS" BASIS, + * 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. @@ -397,10 +397,10 @@ public class ZenModeFilteringTest extends UiServiceTestCase { Bundle inputWrong = makeExtrasBundleWithPeople(new String[]{"mailto:nope"}); assertTrue(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - inputMatches, null, 0, 0)); + inputMatches, null, 0, 0, 0)); assertFalse(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - inputWrong, null, 0, 0)); + inputWrong, null, 0, 0, 0)); } @Test @@ -428,19 +428,19 @@ public class ZenModeFilteringTest extends UiServiceTestCase { assertTrue("identical numbers should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - identical, null, 0, 0)); + identical, null, 0, 0, 0)); assertTrue("equivalent but non-identical numbers should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - same, null, 0, 0)); + same, null, 0, 0, 0)); assertFalse("non-equivalent numbers should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - different, null, 0, 0)); + different, null, 0, 0, 0)); assertFalse("non-tel strings should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - garbage, null, 0, 0)); + garbage, null, 0, 0, 0)); } @Test @@ -469,23 +469,23 @@ public class ZenModeFilteringTest extends UiServiceTestCase { assertTrue("same number 1 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - same1, null, 0, 0)); + same1, null, 0, 0, 0)); assertTrue("same number 2 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - same2, null, 0, 0)); + same2, null, 0, 0, 0)); assertTrue("same number 3 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - same3, null, 0, 0)); + same3, null, 0, 0, 0)); assertFalse("different number 1 should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - different1, null, 0, 0)); + different1, null, 0, 0, 0)); assertFalse("different number 2 should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - different2, null, 0, 0)); + different2, null, 0, 0, 0)); } @Test @@ -516,14 +516,14 @@ public class ZenModeFilteringTest extends UiServiceTestCase { assertTrue("contact number 1 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - tel1, null, 0, 0)); + tel1, null, 0, 0, 0)); assertTrue("contact number 2 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - tel2, null, 0, 0)); + tel2, null, 0, 0, 0)); assertFalse("different number should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, - different, null, 0, 0)); + different, null, 0, 0, 0)); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 05cc0cf14081..46b4b76dc12f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -24,6 +24,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -41,6 +42,7 @@ import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -52,16 +54,20 @@ import static org.mockito.Mockito.when; import android.graphics.Rect; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.util.MergedConfiguration; import android.view.IWindowSessionCallback; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsVisibilities; +import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; +import android.window.ClientWindowFrames; import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; @@ -166,6 +172,32 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test + public void testRelayoutExitingWindow() { + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin"); + final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class); + doReturn(true).when(surfaceController).hasSurface(); + spyOn(win); + doReturn(true).when(win).isExitAnimationRunningSelfOrParent(); + win.mWinAnimator.mSurfaceController = surfaceController; + win.mViewVisibility = View.VISIBLE; + win.mHasSurface = true; + win.mActivityRecord.mAppStopped = true; + win.mActivityRecord.mVisibleRequested = false; + win.mActivityRecord.setVisible(false); + mWm.mWindowMap.put(win.mClient.asBinder(), win); + final int w = 100; + final int h = 200; + mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0, + new ClientWindowFrames(), new MergedConfiguration(), new SurfaceControl(), + new InsetsState(), new InsetsSourceControl[0], new Bundle()); + // Because the window is already invisible, it doesn't need to apply exiting animation + // and WMS#tryStartExitingAnimation() will destroy the surface directly. + assertFalse(win.mAnimatingExit); + assertFalse(win.mHasSurface); + assertNull(win.mWinAnimator.mSurfaceController); + } + + @Test public void testMoveWindowTokenToDisplay_NullToken_DoNothing() { mWm.moveWindowTokenToDisplay(null, mDisplayContent.getDisplayId()); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5ad4edf71075..4af8cde4364c 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1175,14 +1175,55 @@ public class CarrierConfigManager { "carrier_data_call_permanent_failure_strings"; /** - * Default APN types that are metered by the carrier - * @hide + * A string array indicating the default APN types that are metered by the carrier. + * + * The string in the array is the name of the APN type. For example, "default" for + * {@link ApnSetting#TYPE_DEFAULT}, "mms" for {@link ApnSetting#TYPE_MMS}, etc. + * + * The default value is {@code {"default", "mms", "dun", "supl"}}. + * + * @see ApnSetting#TYPE_DEFAULT + * @see ApnSetting#TYPE_MMS + * @see ApnSetting#TYPE_SUPL + * @see ApnSetting#TYPE_DUN + * @see ApnSetting#TYPE_HIPRI + * @see ApnSetting#TYPE_FOTA + * @see ApnSetting#TYPE_IMS + * @see ApnSetting#TYPE_CBS + * @see ApnSetting#TYPE_IA + * @see ApnSetting#TYPE_EMERGENCY + * @see ApnSetting#TYPE_MCX + * @see ApnSetting#TYPE_XCAP + * @see ApnSetting#TYPE_BIP + * @see ApnSetting#TYPE_VSIM + * @see ApnSetting#TYPE_ENTERPRISE */ public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS = "carrier_metered_apn_types_strings"; + /** - * Default APN types that are roaming-metered by the carrier - * @hide + * A string array indicating the default APN types that are roaming-metered by the carrier. + * + * The string in the array is the name of the APN type. For example, "default" for + * {@link ApnSetting#TYPE_DEFAULT}, "mms" for {@link ApnSetting#TYPE_MMS}, etc. + * + * The default value is {@code {"default", "mms", "dun", "supl"}}. + * + * @see ApnSetting#TYPE_DEFAULT + * @see ApnSetting#TYPE_MMS + * @see ApnSetting#TYPE_SUPL + * @see ApnSetting#TYPE_DUN + * @see ApnSetting#TYPE_HIPRI + * @see ApnSetting#TYPE_FOTA + * @see ApnSetting#TYPE_IMS + * @see ApnSetting#TYPE_CBS + * @see ApnSetting#TYPE_IA + * @see ApnSetting#TYPE_EMERGENCY + * @see ApnSetting#TYPE_MCX + * @see ApnSetting#TYPE_XCAP + * @see ApnSetting#TYPE_BIP + * @see ApnSetting#TYPE_VSIM + * @see ApnSetting#TYPE_ENTERPRISE */ public static final String KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS = "carrier_metered_roaming_apn_types_strings"; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index d5b4434a136c..c9a63c654a40 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -16444,7 +16444,12 @@ public class TelephonyManager { * the appropriate callback method on the callback object and passes the current (updated) * values. * <p> - * + * Note: Be aware of the permission requirements stated on the {@link TelephonyCallback} + * listeners you implement. Your application must be granted these permissions in order to + * register a {@link TelephonyCallback} which requires them; a {@link SecurityException} will be + * thrown if you do not hold the required permissions for all {@link TelephonyCallback} + * listeners you implement. + * <p> * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, * applies to the given subId. Otherwise, applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple |