diff options
286 files changed, 9061 insertions, 4257 deletions
diff --git a/Android.bp b/Android.bp index 1c9eac9f7d39..7e68986ebeec 100644 --- a/Android.bp +++ b/Android.bp @@ -369,6 +369,7 @@ filegroup { ":framework_native_aidl", ":gatekeeper_aidl", ":gsiservice_aidl", + ":idmap2_core_aidl", ":incidentcompanion_aidl", ":inputconstants_aidl", ":installd_aidl", diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 271129bee7c5..c369801a091f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -108,7 +108,7 @@ public class AppSearchManagerService extends SystemService { schemasPackageAccessibleBundles.entrySet()) { List<PackageIdentifier> packageIdentifiers = new ArrayList<>(entry.getValue().size()); - for (int i = 0; i < packageIdentifiers.size(); i++) { + for (int i = 0; i < entry.getValue().size(); i++) { packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i))); } schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index 8bff7207a553..2f1817ec82a7 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -317,7 +317,7 @@ public final class AppSearchImpl { } Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible = - new ArrayMap<>(schemasNotPlatformSurfaceable.size()); + new ArrayMap<>(schemasPackageAccessible.size()); for (Map.Entry<String, List<PackageIdentifier>> entry : schemasPackageAccessible.entrySet()) { prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue()); diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java index eb1623e5dffc..6595d8d4abba 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java @@ -37,8 +37,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via - * a consistent interface. + * This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via a + * consistent interface. + * * @hide */ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { @@ -47,7 +48,13 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { @NonNull public static ListenableFuture<GlobalSearchSessionShim> createGlobalSearchSession() { - Context context = ApplicationProvider.getApplicationContext(); + return createGlobalSearchSession(ApplicationProvider.getApplicationContext()); + } + + /** Only for use when called from a non-instrumented context. */ + @NonNull + public static ListenableFuture<GlobalSearchSessionShim> createGlobalSearchSession( + @NonNull Context context) { AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class); SettableFuture<AppSearchResult<GlobalSearchSession>> future = SettableFuture.create(); ExecutorService executor = Executors.newCachedThreadPool(); @@ -62,7 +69,6 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { @NonNull GlobalSearchSession session, @NonNull ExecutorService executor) { mGlobalSearchSession = Preconditions.checkNotNull(session); mExecutor = Preconditions.checkNotNull(executor); - } @NonNull diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 82ee5d8c1c7c..71fe55fb0640 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -212,7 +212,7 @@ public class JobSchedulerService extends com.android.server.SystemService final JobPackageTracker mJobPackageTracker = new JobPackageTracker(); final JobConcurrencyManager mConcurrencyManager; - static final int MSG_JOB_EXPIRED = 0; + static final int MSG_CHECK_INDIVIDUAL_JOB = 0; static final int MSG_CHECK_JOB = 1; static final int MSG_STOP_JOB = 2; static final int MSG_CHECK_JOB_GREEDY = 3; @@ -1711,6 +1711,12 @@ public class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); } + JobStatus newJs = mJobs.getJobByUidAndJobId(jobStatus.getUid(), jobStatus.getJobId()); + if (newJs != null) { + // This job was stopped because the app scheduled a new job with the same job ID. + // Check if the new job is ready to run. + mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, newJs).sendToTarget(); + } return; } @@ -1748,7 +1754,11 @@ public class JobSchedulerService extends com.android.server.SystemService @Override public void onRunJobNow(JobStatus jobStatus) { - mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); + if (jobStatus == null) { + mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); + } else { + mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, jobStatus).sendToTarget(); + } } final private class JobHandler extends Handler { @@ -1764,18 +1774,15 @@ public class JobSchedulerService extends com.android.server.SystemService return; } switch (message.what) { - case MSG_JOB_EXPIRED: { - JobStatus runNow = (JobStatus) message.obj; - // runNow can be null, which is a controller's way of indicating that its - // state is such that all ready jobs should be run immediately. - if (runNow != null) { - if (!isCurrentlyActiveLocked(runNow) - && isReadyToBeExecutedLocked(runNow)) { - mJobPackageTracker.notePending(runNow); - addOrderedItem(mPendingJobs, runNow, sPendingJobComparator); + case MSG_CHECK_INDIVIDUAL_JOB: { + JobStatus js = (JobStatus) message.obj; + if (js != null) { + if (isReadyToBeExecutedLocked(js)) { + mJobPackageTracker.notePending(js); + addOrderedItem(mPendingJobs, js, sPendingJobComparator); } } else { - queueReadyJobsForExecutionLocked(); + Slog.e(TAG, "Given null job to check individually"); } } break; case MSG_CHECK_JOB: @@ -1909,12 +1916,10 @@ public class JobSchedulerService extends com.android.server.SystemService // This method will check and capture all ready jobs, so we don't need to keep any messages // in the queue. mHandler.removeMessages(MSG_CHECK_JOB_GREEDY); + mHandler.removeMessages(MSG_CHECK_INDIVIDUAL_JOB); // MSG_CHECK_JOB is a weaker form of _GREEDY. Since we're checking and queueing all ready // jobs, we don't need to keep any MSG_CHECK_JOB messages in the queue. mHandler.removeMessages(MSG_CHECK_JOB); - // This method will capture all expired jobs that are ready, so there's no need to keep - // the _EXPIRED messages in the queue. - mHandler.removeMessages(MSG_JOB_EXPIRED); if (DEBUG) { Slog.d(TAG, "queuing all ready jobs for execution:"); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java index 50723c7c2841..131a6d4f4791 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java @@ -107,8 +107,6 @@ public final class ContentObserverController extends StateController { taskStatus.contentObserverJobInstance.mChangedUris.add(uri); } } - taskStatus.changedAuthorities = null; - taskStatus.changedUris = null; } taskStatus.changedAuthorities = null; taskStatus.changedUris = null; diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 50f400122fe1..3bad889f1715 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -51,13 +51,18 @@ cc_library { static: { enabled: false, }, + static_libs: [ + "libidmap2_protos", + ], shared_libs: [ "libandroidfw", "libbase", "libcutils", + "libidmap2_policies", + "libprotobuf-cpp-lite", "libutils", + "libz", "libziparchive", - "libidmap2_policies", ], }, host: { @@ -68,15 +73,30 @@ cc_library { "libandroidfw", "libbase", "libcutils", + "libidmap2_policies", + "libidmap2_protos", + "libprotobuf-cpp-lite", "libutils", + "libz", "libziparchive", - "libidmap2_policies", ], }, }, } cc_library { + name: "libidmap2_protos", + srcs: [ + "libidmap2/proto/*.proto", + ], + host_supported: true, + proto: { + type: "lite", + export_proto_headers: true, + }, +} + +cc_library { name: "libidmap2_policies", defaults: [ "idmap2_defaults", @@ -88,9 +108,6 @@ cc_library { enabled: true, }, android: { - static: { - enabled: false, - }, shared_libs: [ "libandroidfw", ], @@ -119,6 +136,7 @@ cc_test { srcs: [ "tests/BinaryStreamVisitorTests.cpp", "tests/CommandLineOptionsTests.cpp", + "tests/FabricatedOverlayTests.cpp", "tests/FileUtilsTests.cpp", "tests/Idmap2BinaryTests.cpp", "tests/IdmapTests.cpp", @@ -130,20 +148,27 @@ cc_test { "tests/ResourceUtilsTests.cpp", "tests/ResultTests.cpp", "tests/XmlParserTests.cpp", - "tests/ZipFileTests.cpp", ], - static_libs: ["libgmock"], + required: [ + "idmap2", + ], + static_libs: [ + "libgmock", + "libidmap2_protos", + ], target: { android: { shared_libs: [ "libandroidfw", "libbase", "libidmap2", + "libidmap2_policies", "liblog", + "libprotobuf-cpp-lite", "libutils", "libz", + "libz", "libziparchive", - "libidmap2_policies", ], }, host: { @@ -152,10 +177,11 @@ cc_test { "libbase", "libcutils", "libidmap2", + "libidmap2_policies", "liblog", + "libprotobuf-cpp-lite", "libutils", "libziparchive", - "libidmap2_policies", ], shared_libs: [ "libz", @@ -189,6 +215,9 @@ cc_binary { "idmap2/Lookup.cpp", "idmap2/Main.cpp", ], + static_libs: [ + "libidmap2_protos", + ], target: { android: { shared_libs: [ @@ -196,9 +225,11 @@ cc_binary { "libbase", "libcutils", "libidmap2", + "libidmap2_policies", + "libprotobuf-cpp-lite", "libutils", + "libz", "libziparchive", - "libidmap2_policies", ], }, host: { @@ -207,10 +238,11 @@ cc_binary { "libbase", "libcutils", "libidmap2", + "libidmap2_policies", "liblog", + "libprotobuf-cpp-lite", "libutils", "libziparchive", - "libidmap2_policies", ], shared_libs: [ "libz", @@ -236,11 +268,14 @@ cc_binary { "libbinder", "libcutils", "libidmap2", + "libidmap2_policies", + "libprotobuf-cpp-lite", "libutils", "libziparchive", - "libidmap2_policies", ], static_libs: [ + "libc++fs", + "libidmap2_protos", "libidmap2daidl", ], init_rc: ["idmap2d/idmap2d.rc"], @@ -248,28 +283,41 @@ cc_binary { cc_library_static { name: "libidmap2daidl", - defaults: [ - "idmap2_defaults", - ], - tidy: false, - host_supported: false, srcs: [ ":idmap2_aidl", + ":idmap2_core_aidl", + ], + header_libs: [ + "libbinder_headers", ], shared_libs: [ "libbase", ], aidl: { export_aidl_headers: true, + local_include_dirs: [ + "idmap2d/aidl/core", + "idmap2d/aidl/services/", + ], }, } filegroup { + name: "idmap2_core_aidl", + srcs: [ + "idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl", + "idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl", + "idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl", + ], + path: "idmap2d/aidl/core/", +} + +filegroup { name: "idmap2_aidl", srcs: [ - "idmap2d/aidl/android/os/IIdmap2.aidl", + "idmap2d/aidl/services/android/os/IIdmap2.aidl", ], - path: "idmap2d/aidl", + path: "idmap2d/aidl/services/", } aidl_interface { @@ -281,7 +329,7 @@ aidl_interface { filegroup { name: "overlayable_policy_aidl_files", srcs: [ - "idmap2d/aidl/android/os/OverlayablePolicy.aidl", + "idmap2d/aidl/services/android/os/OverlayablePolicy.aidl", ], - path: "idmap2d/aidl", + path: "idmap2d/aidl/services/", } diff --git a/cmds/idmap2/idmap2/CommandUtils.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp index 09867f3a9c20..bf30a76d581a 100644 --- a/cmds/idmap2/idmap2/CommandUtils.cpp +++ b/cmds/idmap2/idmap2/CommandUtils.cpp @@ -25,7 +25,9 @@ using android::idmap2::Error; using android::idmap2::IdmapHeader; +using android::idmap2::OverlayResourceContainer; using android::idmap2::Result; +using android::idmap2::TargetResourceContainer; using android::idmap2::Unit; Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path, @@ -39,11 +41,20 @@ Result<Unit> Verify(const std::string& idmap_path, const std::string& target_pat return Error("failed to parse idmap header"); } - const auto header_ok = header->IsUpToDate(target_path, overlay_path, overlay_name, - fulfilled_policies, enforce_overlayable); + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return Error("failed to load target '%s'", target_path.c_str()); + } + + auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return Error("failed to load overlay '%s'", overlay_path.c_str()); + } + + const auto header_ok = header->IsUpToDate(**target, **overlay, overlay_name, fulfilled_policies, + enforce_overlayable); if (!header_ok) { return Error(header_ok.GetError(), "idmap not up to date"); } - return Unit{}; } diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index c93c717a15d2..977a0bbadafb 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -20,7 +20,6 @@ #include <fstream> #include <memory> #include <ostream> -#include <string> #include <vector> #include "androidfw/ResourceTypes.h" @@ -31,12 +30,13 @@ #include "idmap2/PolicyUtils.h" #include "idmap2/SysTrace.h" -using android::ApkAssets; using android::idmap2::BinaryStreamVisitor; using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::Idmap; +using android::idmap2::OverlayResourceContainer; using android::idmap2::Result; +using android::idmap2::TargetResourceContainer; using android::idmap2::Unit; using android::idmap2::utils::kIdmapFilePermissionMask; using android::idmap2::utils::PoliciesToBitmaskResult; @@ -93,18 +93,18 @@ Result<Unit> Create(const std::vector<std::string>& args) { fulfilled_policies |= PolicyFlags::PUBLIC; } - const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error("failed to load apk %s", target_apk_path.c_str()); + const auto target = TargetResourceContainer::FromPath(target_apk_path); + if (!target) { + return Error("failed to load target '%s'", target_apk_path.c_str()); } - const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return Error("failed to load apk %s", overlay_apk_path.c_str()); + const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + if (!overlay) { + return Error("failed to load apk overlay '%s'", overlay_apk_path.c_str()); } - const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, overlay_name, - fulfilled_policies, !ignore_overlayable); + const auto idmap = Idmap::FromContainers(**target, **overlay, overlay_name, fulfilled_policies, + !ignore_overlayable); if (!idmap) { return Error(idmap.GetError(), "failed to create idmap"); } @@ -112,13 +112,14 @@ Result<Unit> Create(const std::vector<std::string>& args) { umask(kIdmapFilePermissionMask); std::ofstream fout(idmap_path); if (fout.fail()) { - return Error("failed to open idmap path %s", idmap_path.c_str()); + return Error("failed to open idmap path '%s'", idmap_path.c_str()); } + BinaryStreamVisitor visitor(fout); (*idmap)->accept(&visitor); fout.close(); if (fout.fail()) { - return Error("failed to write to idmap path %s", idmap_path.c_str()); + return Error("failed to write to idmap path '%s'", idmap_path.c_str()); } return Unit{}; diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp index 5db391caac30..953d99f8dcfb 100644 --- a/cmds/idmap2/idmap2/CreateMultiple.cpp +++ b/cmds/idmap2/idmap2/CreateMultiple.cpp @@ -34,13 +34,14 @@ #include "idmap2/PolicyUtils.h" #include "idmap2/SysTrace.h" -using android::ApkAssets; using android::base::StringPrintf; using android::idmap2::BinaryStreamVisitor; using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::Idmap; +using android::idmap2::OverlayResourceContainer; using android::idmap2::Result; +using android::idmap2::TargetResourceContainer; using android::idmap2::Unit; using android::idmap2::utils::kIdmapCacheDir; using android::idmap2::utils::kIdmapFilePermissionMask; @@ -91,9 +92,9 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) { fulfilled_policies |= PolicyFlags::PUBLIC; } - const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error("failed to load apk %s", target_apk_path.c_str()); + const auto target = TargetResourceContainer::FromPath(target_apk_path); + if (!target) { + return Error("failed to load target '%s'", target_apk_path.c_str()); } std::vector<std::string> idmap_paths; @@ -108,14 +109,14 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) { // TODO(b/175014391): Support multiple overlay tags in OverlayConfig if (!Verify(idmap_path, target_apk_path, overlay_apk_path, "", fulfilled_policies, !ignore_overlayable)) { - const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { + const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + if (!overlay) { LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str(); continue; } - const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", fulfilled_policies, - !ignore_overlayable); + const auto idmap = + Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable); if (!idmap) { LOG(WARNING) << "failed to create idmap"; continue; diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 43a1951a5ba9..f41e57cc66d6 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -37,7 +37,6 @@ #include "idmap2/Result.h" #include "idmap2/SysTrace.h" #include "idmap2/XmlParser.h" -#include "idmap2/ZipFile.h" #include "utils/String16.h" #include "utils/String8.h" @@ -52,10 +51,10 @@ using android::base::StringPrintf; using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::IdmapHeader; +using android::idmap2::OverlayResourceContainer; using android::idmap2::ResourceId; using android::idmap2::Result; using android::idmap2::Unit; -using android::idmap2::utils::ExtractOverlayManifestInfo; namespace { @@ -195,12 +194,17 @@ Result<Unit> Lookup(const std::vector<std::string>& args) { } apk_assets.push_back(std::move(target_apk)); - auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath(), - idmap_header->GetOverlayName()); + auto overlay = OverlayResourceContainer::FromPath(idmap_header->GetOverlayPath()); + if (!overlay) { + return overlay.GetError(); + } + + auto manifest_info = (*overlay)->FindOverlayInfo(idmap_header->GetOverlayName()); if (!manifest_info) { return manifest_info.GetError(); } - target_package_name = manifest_info->target_package; + + target_package_name = (*manifest_info).target_package; } else if (target_path != idmap_header->GetTargetPath()) { return Error("different target APKs (expected target APK %s but %s has target APK %s)", target_path.c_str(), idmap_path.c_str(), idmap_header->GetTargetPath().c_str()); diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 93537d32299b..05336baf9217 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -18,10 +18,10 @@ #include <sys/stat.h> // umask #include <sys/types.h> // umask -#include <unistd.h> #include <cerrno> #include <cstring> +#include <filesystem> #include <fstream> #include <memory> #include <ostream> @@ -35,19 +35,20 @@ #include "idmap2/Idmap.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/ZipFile.h" -#include "utils/String8.h" using android::IPCThreadState; using android::base::StringPrintf; using android::binder::Status; using android::idmap2::BinaryStreamVisitor; -using android::idmap2::GetPackageCrc; +using android::idmap2::FabricatedOverlay; +using android::idmap2::FabricatedOverlayContainer; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; -using android::idmap2::ZipFile; +using android::idmap2::OverlayResourceContainer; +using android::idmap2::TargetResourceContainer; using android::idmap2::utils::kIdmapCacheDir; using android::idmap2::utils::kIdmapFilePermissionMask; +using android::idmap2::utils::RandomStringForPath; using android::idmap2::utils::UidHasWriteAccessToPath; using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; @@ -69,39 +70,24 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) { return static_cast<PolicyBitmask>(arg); } -Status GetCrc(const std::string& apk_path, uint32_t* out_crc) { - const auto zip = ZipFile::Open(apk_path); - if (!zip) { - return error(StringPrintf("failed to open apk %s", apk_path.c_str())); - } - - const auto crc = GetPackageCrc(*zip); - if (!crc) { - return error(crc.GetErrorMessage()); - } - - *out_crc = *crc; - return ok(); -} - } // namespace namespace android::os { -Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path, +Status Idmap2Service::getIdmapPath(const std::string& overlay_path, int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) { assert(_aidl_return); - SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_apk_path; - *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_path; + *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); return ok(); } -Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, - int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { +Status Idmap2Service::removeIdmap(const std::string& overlay_path, int32_t user_id ATTRIBUTE_UNUSED, + bool* _aidl_return) { assert(_aidl_return); - SYSTRACE << "Idmap2Service::removeIdmap " << overlay_apk_path; + SYSTRACE << "Idmap2Service::removeIdmap " << overlay_path; const uid_t uid = IPCThreadState::self()->getCallingUid(); - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); if (!UidHasWriteAccessToPath(uid, idmap_path)) { *_aidl_return = false; return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access", @@ -115,93 +101,88 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, return ok(); } -Status Idmap2Service::verifyIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, +Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::string& overlay_path, + const std::string& overlay_name, int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { - SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path; + SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_path; assert(_aidl_return); - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); std::ifstream fin(idmap_path); const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin); fin.close(); if (!header) { *_aidl_return = false; - return error("failed to parse idmap header"); + LOG(WARNING) << "failed to parse idmap header of '" << idmap_path << "'"; + return ok(); } - uint32_t target_crc; - if (target_apk_path == kFrameworkPath && android_crc_) { - target_crc = *android_crc_; - } else { - auto target_crc_status = GetCrc(target_apk_path, &target_crc); - if (!target_crc_status.isOk()) { - *_aidl_return = false; - return target_crc_status; - } - - // Loading the framework zip can take several milliseconds. Cache the crc of the framework - // resource APK to reduce repeated work during boot. - if (target_apk_path == kFrameworkPath) { - android_crc_ = target_crc; - } + const auto target = GetTargetContainer(target_path); + if (!target) { + *_aidl_return = false; + LOG(WARNING) << "failed to load target '" << target_path << "'"; + return ok(); } - uint32_t overlay_crc; - auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc); - if (!overlay_crc_status.isOk()) { + const auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { *_aidl_return = false; - return overlay_crc_status; + LOG(WARNING) << "failed to load overlay '" << overlay_path << "'"; + return ok(); } - // TODO(162841629): Support passing overlay name to idmap2d verify auto up_to_date = - header->IsUpToDate(target_apk_path, overlay_apk_path, "", target_crc, overlay_crc, + header->IsUpToDate(*GetPointer(*target), **overlay, overlay_name, ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable); *_aidl_return = static_cast<bool>(up_to_date); - return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage()); + if (!up_to_date) { + LOG(WARNING) << "idmap '" << idmap_path + << "' not up to date : " << up_to_date.GetErrorMessage(); + } + return ok(); } -Status Idmap2Service::createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, +Status Idmap2Service::createIdmap(const std::string& target_path, const std::string& overlay_path, + const std::string& overlay_name, int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED, std::optional<std::string>* _aidl_return) { assert(_aidl_return); - SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path; + SYSTRACE << "Idmap2Service::createIdmap " << target_path << " " << overlay_path; _aidl_return->reset(); const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies); - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); const uid_t uid = IPCThreadState::self()->getCallingUid(); if (!UidHasWriteAccessToPath(uid, idmap_path)) { return error(base::StringPrintf("will not write to %s: calling uid %d lacks write accesss", idmap_path.c_str(), uid)); } - const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return error("failed to load apk " + target_apk_path); + // idmap files are mapped with mmap in libandroidfw. Deleting and recreating the idmap guarantees + // that existing memory maps will continue to be valid and unaffected. The file must be deleted + // before attempting to create the idmap, so that if idmap creation fails, the overlay will no + // longer be usable. + unlink(idmap_path.c_str()); + + const auto target = GetTargetContainer(target_path); + if (!target) { + return error("failed to load target '%s'" + target_path); } - const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return error("failed to load apk " + overlay_apk_path); + const auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return error("failed to load apk overlay '%s'" + overlay_path); } - // TODO(162841629): Support passing overlay name to idmap2d create - const auto idmap = - Idmap::FromApkAssets(*target_apk, *overlay_apk, "", policy_bitmask, enforce_overlayable); + const auto idmap = Idmap::FromContainers(*GetPointer(*target), **overlay, overlay_name, + policy_bitmask, enforce_overlayable); if (!idmap) { return error(idmap.GetErrorMessage()); } - // idmap files are mapped with mmap in libandroidfw. Deleting and recreating the idmap guarantees - // that existing memory maps will continue to be valid and unaffected. - unlink(idmap_path.c_str()); - umask(kIdmapFilePermissionMask); std::ofstream fout(idmap_path); if (fout.fail()) { @@ -220,4 +201,155 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, return ok(); } +idmap2::Result<Idmap2Service::TargetResourceContainerPtr> Idmap2Service::GetTargetContainer( + const std::string& target_path) { + if (target_path == kFrameworkPath) { + if (framework_apk_cache_ == nullptr) { + // Initialize the framework APK cache. + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return target.GetError(); + } + framework_apk_cache_ = std::move(*target); + } + return {framework_apk_cache_.get()}; + } + + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return target.GetError(); + } + return {std::move(*target)}; +} + +Status Idmap2Service::createFabricatedOverlay( + const os::FabricatedOverlayInternal& overlay, + std::optional<os::FabricatedOverlayInfo>* _aidl_return) { + idmap2::FabricatedOverlay::Builder builder(overlay.packageName, overlay.overlayName, + overlay.targetPackageName); + if (!overlay.targetOverlayable.empty()) { + builder.SetOverlayable(overlay.targetOverlayable); + } + + for (const auto& res : overlay.entries) { + builder.SetResourceValue(res.resourceName, res.dataType, res.data); + } + + // Generate the file path of the fabricated overlay and ensure it does not collide with an + // existing path. Re-registering a fabricated overlay will always result in an updated path. + std::string path; + std::string file_name; + do { + constexpr size_t kSuffixLength = 4; + const std::string random_suffix = RandomStringForPath(kSuffixLength); + file_name = StringPrintf("%s-%s-%s.frro", overlay.packageName.c_str(), + overlay.overlayName.c_str(), random_suffix.c_str()); + path = StringPrintf("%s/%s", kIdmapCacheDir, file_name.c_str()); + + // Invoking std::filesystem::exists with a file name greater than 255 characters will cause this + // process to abort since the name exceeds the maximum file name size. + const size_t kMaxFileNameLength = 255; + if (file_name.size() > kMaxFileNameLength) { + return error( + base::StringPrintf("fabricated overlay file name '%s' longer than %zu characters", + file_name.c_str(), kMaxFileNameLength)); + } + } while (std::filesystem::exists(path)); + + const uid_t uid = IPCThreadState::self()->getCallingUid(); + if (!UidHasWriteAccessToPath(uid, path)) { + return error(base::StringPrintf("will not write to %s: calling uid %d lacks write access", + path.c_str(), uid)); + } + + // Persist the fabricated overlay. + umask(kIdmapFilePermissionMask); + std::ofstream fout(path); + if (fout.fail()) { + return error("failed to open frro path " + path); + } + const auto frro = builder.Build(); + if (!frro) { + return error(StringPrintf("failed to serialize '%s:%s': %s", overlay.packageName.c_str(), + overlay.overlayName.c_str(), frro.GetErrorMessage().c_str())); + } + auto result = frro->ToBinaryStream(fout); + if (!result) { + unlink(path.c_str()); + return error("failed to write to frro path " + path + ": " + result.GetErrorMessage()); + } + if (fout.fail()) { + unlink(path.c_str()); + return error("failed to write to frro path " + path); + } + + os::FabricatedOverlayInfo out_info; + out_info.packageName = overlay.packageName; + out_info.overlayName = overlay.overlayName; + out_info.targetPackageName = overlay.targetPackageName; + out_info.targetOverlayable = overlay.targetOverlayable; + out_info.path = path; + *_aidl_return = out_info; + return ok(); +} + +Status Idmap2Service::getFabricatedOverlayInfos( + std::vector<os::FabricatedOverlayInfo>* _aidl_return) { + for (const auto& entry : std::filesystem::directory_iterator(kIdmapCacheDir)) { + if (!android::IsFabricatedOverlay(entry.path())) { + continue; + } + + const auto overlay = FabricatedOverlayContainer::FromPath(entry.path()); + if (!overlay) { + // This is a sign something went wrong. + LOG(ERROR) << "Failed to open '" << entry.path() << "': " << overlay.GetErrorMessage(); + continue; + } + + const auto info = (*overlay)->GetManifestInfo(); + os::FabricatedOverlayInfo out_info; + out_info.packageName = info.package_name; + out_info.overlayName = info.name; + out_info.targetPackageName = info.target_package; + out_info.targetOverlayable = info.target_name; + out_info.path = entry.path(); + _aidl_return->emplace_back(std::move(out_info)); + } + + return ok(); +} + +binder::Status Idmap2Service::deleteFabricatedOverlay(const std::string& overlay_path, + bool* _aidl_return) { + SYSTRACE << "Idmap2Service::deleteFabricatedOverlay " << overlay_path; + const uid_t uid = IPCThreadState::self()->getCallingUid(); + + if (!UidHasWriteAccessToPath(uid, overlay_path)) { + *_aidl_return = false; + return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access", + overlay_path.c_str(), uid)); + } + + const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path); + if (!UidHasWriteAccessToPath(uid, idmap_path)) { + *_aidl_return = false; + return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access", + idmap_path.c_str(), uid)); + } + + if (unlink(overlay_path.c_str()) != 0) { + *_aidl_return = false; + return error("failed to unlink " + overlay_path + ": " + strerror(errno)); + } + + if (unlink(idmap_path.c_str()) != 0) { + *_aidl_return = false; + return error("failed to unlink " + idmap_path + ": " + strerror(errno)); + } + + *_aidl_return = true; + return ok(); +} + } // namespace android::os diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index 0127e874b444..4d16ff39884b 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -18,12 +18,14 @@ #define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_ #include <android-base/unique_fd.h> +#include <android/os/BnIdmap2.h> +#include <android/os/FabricatedOverlayInfo.h> #include <binder/BinderService.h> +#include <idmap2/ResourceContainer.h> +#include <idmap2/Result.h> #include <string> -#include "android/os/BnIdmap2.h" - namespace android::os { class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { @@ -32,27 +34,58 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { return "idmap"; } - binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id, + binder::Status getIdmapPath(const std::string& overlay_path, int32_t user_id, std::string* _aidl_return) override; - binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id, + binder::Status removeIdmap(const std::string& overlay_path, int32_t user_id, bool* _aidl_return) override; - binder::Status verifyIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, + binder::Status verifyIdmap(const std::string& target_path, const std::string& overlay_path, + const std::string& overlay_name, int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id, bool* _aidl_return) override; - binder::Status createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t fulfilled_policies, + binder::Status createIdmap(const std::string& target_path, const std::string& overlay_path, + const std::string& overlay_name, int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id, std::optional<std::string>* _aidl_return) override; + binder::Status createFabricatedOverlay( + const os::FabricatedOverlayInternal& overlay, + std::optional<os::FabricatedOverlayInfo>* _aidl_return) override; + + binder::Status deleteFabricatedOverlay(const std::string& overlay_path, + bool* _aidl_return) override; + + binder::Status getFabricatedOverlayInfos( + std::vector<os::FabricatedOverlayInfo>* _aidl_return) override; + private: - // Cache the crc of the android framework package since the crc cannot change without a reboot. - std::optional<uint32_t> android_crc_; + // idmap2d is killed after a period of inactivity, so any information stored on this class should + // be able to be recalculated if idmap2 dies and restarts. + std::unique_ptr<idmap2::TargetResourceContainer> framework_apk_cache_; + + std::vector<os::FabricatedOverlayInfo> fabricated_overlays_; + + template <typename T> + using MaybeUniquePtr = std::variant<std::unique_ptr<T>, T*>; + + using TargetResourceContainerPtr = MaybeUniquePtr<idmap2::TargetResourceContainer>; + idmap2::Result<TargetResourceContainerPtr> GetTargetContainer(const std::string& target_path); + + template <typename T> + WARN_UNUSED static const T* GetPointer(const MaybeUniquePtr<T>& ptr); }; +template <typename T> +const T* Idmap2Service::GetPointer(const MaybeUniquePtr<T>& ptr) { + auto u = std::get_if<T*>(&ptr); + if (u != nullptr) { + return *u; + } + return std::get<std::unique_ptr<T>>(ptr).get(); +} + } // namespace android::os #endif // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_ diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl new file mode 100644 index 000000000000..6375d24845e2 --- /dev/null +++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * @hide + */ +parcelable FabricatedOverlayInfo { + @utf8InCpp String path; + @utf8InCpp String packageName; + @utf8InCpp String overlayName; + @utf8InCpp String targetPackageName; + @utf8InCpp String targetOverlayable; +}
\ No newline at end of file diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl new file mode 100644 index 000000000000..f67d8beeed3a --- /dev/null +++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.FabricatedOverlayInternalEntry; + +/** + * @hide + */ +parcelable FabricatedOverlayInternal { + @utf8InCpp String packageName; + @utf8InCpp String overlayName; + @utf8InCpp String targetPackageName; + @utf8InCpp String targetOverlayable; + List<FabricatedOverlayInternalEntry> entries; +}
\ No newline at end of file diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl new file mode 100644 index 000000000000..6c2af274ae32 --- /dev/null +++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * @hide + */ +parcelable FabricatedOverlayInternalEntry { + @utf8InCpp String resourceName; + int dataType; + int data; +}
\ No newline at end of file diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl index 156f1d70e015..35bca98bcf69 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl @@ -16,6 +16,9 @@ package android.os; +import android.os.FabricatedOverlayInternal; +import android.os.FabricatedOverlayInfo; + /** * @hide */ @@ -23,13 +26,18 @@ interface IIdmap2 { @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId); boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId); boolean verifyIdmap(@utf8InCpp String targetApkPath, - @utf8InCpp String overlayApkPath, + @utf8InCpp String overlayApkPath, + @utf8InCpp String overlayName, int fulfilledPolicies, boolean enforceOverlayable, int userId); @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, + @utf8InCpp String overlayName, int fulfilledPolicies, boolean enforceOverlayable, int userId); + @nullable FabricatedOverlayInfo createFabricatedOverlay(in FabricatedOverlayInternal overlay); + List<FabricatedOverlayInfo> getFabricatedOverlayInfos(); + boolean deleteFabricatedOverlay(@utf8InCpp String path); } diff --git a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl b/cmds/idmap2/idmap2d/aidl/services/android/os/OverlayablePolicy.aidl index 403d8c55de16..403d8c55de16 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl +++ b/cmds/idmap2/idmap2d/aidl/services/android/os/OverlayablePolicy.aidl diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h new file mode 100644 index 000000000000..be687d98081e --- /dev/null +++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H +#define IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H + +#include <libidmap2/proto/fabricated_v1.pb.h> + +#include <iostream> +#include <map> +#include <memory> +#include <unordered_map> + +#include "idmap2/ResourceContainer.h" +#include "idmap2/Result.h" + +namespace android::idmap2 { + +struct FabricatedOverlay { + struct Builder { + Builder(const std::string& package_name, const std::string& name, + const std::string& target_package_name); + + Builder& SetOverlayable(const std::string& name); + + Builder& SetResourceValue(const std::string& resource_name, uint8_t data_type, + uint32_t data_value); + + WARN_UNUSED Result<FabricatedOverlay> Build(); + + private: + struct Entry { + std::string resource_name; + DataType data_type; + DataValue data_value; + }; + + std::string package_name_; + std::string name_; + std::string target_package_name_; + std::string target_overlayable_; + std::vector<Entry> entries_; + }; + + Result<Unit> ToBinaryStream(std::ostream& stream) const; + static Result<FabricatedOverlay> FromBinaryStream(std::istream& stream); + + private: + struct SerializedData { + std::unique_ptr<uint8_t[]> data; + size_t data_size; + uint32_t crc; + }; + + Result<SerializedData*> InitializeData() const; + Result<uint32_t> GetCrc() const; + + FabricatedOverlay(pb::FabricatedOverlay&& overlay, std::optional<uint32_t> crc_from_disk = {}); + + pb::FabricatedOverlay overlay_pb_; + std::optional<uint32_t> crc_from_disk_; + mutable std::optional<SerializedData> data_; + + friend struct FabricatedOverlayContainer; +}; + +struct FabricatedOverlayContainer : public OverlayResourceContainer { + static Result<std::unique_ptr<FabricatedOverlayContainer>> FromPath(std::string path); + static std::unique_ptr<FabricatedOverlayContainer> FromOverlay(FabricatedOverlay&& overlay); + + WARN_UNUSED OverlayManifestInfo GetManifestInfo() const; + + // inherited from OverlayResourceContainer + WARN_UNUSED Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override; + WARN_UNUSED Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override; + + // inherited from ResourceContainer + WARN_UNUSED Result<uint32_t> GetCrc() const override; + WARN_UNUSED const std::string& GetPath() const override; + WARN_UNUSED Result<std::string> GetResourceName(ResourceId id) const override; + + ~FabricatedOverlayContainer() override; + + private: + FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path); + FabricatedOverlay overlay_; + std::string path_; +}; + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h index c4e0e1fd8ef0..2588688de44c 100644 --- a/cmds/idmap2/include/idmap2/FileUtils.h +++ b/cmds/idmap2/include/idmap2/FileUtils.h @@ -17,6 +17,7 @@ #ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_ #define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_ +#include <random> #include <string> namespace android::idmap2::utils { @@ -26,6 +27,8 @@ constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r bool UidHasWriteAccessToPath(uid_t uid, const std::string& path); +std::string RandomStringForPath(size_t length); + } // namespace android::idmap2::utils #endif // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_ diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index 1b815c1197ca..58aff42b1e98 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -23,8 +23,8 @@ * debug_info * data := data_header target_entry* target_inline_entry* overlay_entry* * string_pool - * data_header := target_package_id overlay_package_id padding(2) target_entry_count - * target_inline_entry_count overlay_entry_count string_pool_index + * data_header := target_entry_count target_inline_entry_count overlay_entry_count + * string_pool_index * target_entry := target_id overlay_id * target_inline_entry := target_id Res_value::size padding(1) Res_value::type * Res_value::value @@ -68,11 +68,10 @@ #include <vector> #include "android-base/macros.h" -#include "androidfw/ApkAssets.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" +#include "idmap2/ResourceContainer.h" #include "idmap2/ResourceMapping.h" -#include "idmap2/ZipFile.h" namespace android::idmap2 { @@ -85,9 +84,6 @@ static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic; // current version of the idmap binary format; must be incremented when the format is changed static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion; -// Retrieves a crc generated using all of the files within the zip that can affect idmap generation. -Result<uint32_t> GetPackageCrc(const ZipFile& zip_info); - class IdmapHeader { public: static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream); @@ -135,9 +131,9 @@ class IdmapHeader { // Invariant: anytime the idmap data encoding is changed, the idmap version // field *must* be incremented. Because of this, we know that if the idmap // header is up-to-date the entire file is up-to-date. - Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path, - const std::string& overlay_name, PolicyBitmask fulfilled_policies, - bool enforce_overlayable) const; + Result<Unit> IsUpToDate(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, const std::string& overlay_name, + PolicyBitmask fulfilled_policies, bool enforce_overlayable) const; Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path, const std::string& overlay_name, uint32_t target_crc, @@ -169,14 +165,6 @@ class IdmapData { public: static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream); - inline PackageId GetTargetPackageId() const { - return target_package_id_; - } - - inline PackageId GetOverlayPackageId() const { - return overlay_package_id_; - } - inline uint32_t GetTargetEntryCount() const { return target_entry_count; } @@ -196,8 +184,6 @@ class IdmapData { void accept(Visitor* v) const; private: - PackageId target_package_id_; - PackageId overlay_package_id_; uint32_t target_entry_count; uint32_t target_entry_inline_count; uint32_t overlay_entry_count; @@ -275,11 +261,10 @@ class Idmap { // file is used; change this in the next version of idmap to use a named // package instead; also update FromApkAssets to take additional parameters: // the target and overlay package names - static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const std::string& overlay_name, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable); + static Result<std::unique_ptr<const Idmap>> FromContainers( + const TargetResourceContainer& target, const OverlayResourceContainer& overlay, + const std::string& overlay_name, const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable); const std::unique_ptr<const IdmapHeader>& GetHeader() const { return header_; diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h index 2b4c76124175..4464201a1f2e 100644 --- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h @@ -41,9 +41,8 @@ class PrettyPrintVisitor : public Visitor { private: std::ostream& stream_; - AssetManager2 target_am_; - AssetManager2 overlay_am_; - std::vector<std::unique_ptr<const ApkAssets>> apk_assets_; + std::unique_ptr<TargetResourceContainer> target_; + std::unique_ptr<OverlayResourceContainer> overlay_; }; } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h index 45835164ef8e..ebd0d1eb2fbc 100644 --- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h @@ -49,10 +49,9 @@ class RawPrintVisitor : public Visitor { void pad(size_t padding); std::ostream& stream_; - std::vector<std::unique_ptr<const ApkAssets>> apk_assets_; - AssetManager2 target_am_; - AssetManager2 overlay_am_; size_t offset_; + std::unique_ptr<TargetResourceContainer> target_; + std::unique_ptr<OverlayResourceContainer> overlay_; }; } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/ResourceContainer.h b/cmds/idmap2/include/idmap2/ResourceContainer.h new file mode 100644 index 000000000000..74a6f5666c2a --- /dev/null +++ b/cmds/idmap2/include/idmap2/ResourceContainer.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H +#define IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H + +#include <string> +#include <variant> +#include <vector> + +#include "idmap2/Policies.h" +#include "idmap2/ResourceUtils.h" + +namespace android::idmap2 { + +struct ResourceContainer { + WARN_UNUSED virtual Result<uint32_t> GetCrc() const = 0; + WARN_UNUSED virtual const std::string& GetPath() const = 0; + WARN_UNUSED virtual Result<std::string> GetResourceName(ResourceId id) const = 0; + + virtual ~ResourceContainer() = default; +}; + +struct TargetResourceContainer : public ResourceContainer { + static Result<std::unique_ptr<TargetResourceContainer>> FromPath(std::string path); + + WARN_UNUSED virtual Result<bool> DefinesOverlayable() const = 0; + WARN_UNUSED virtual Result<const android::OverlayableInfo*> GetOverlayableInfo( + ResourceId id) const = 0; + WARN_UNUSED virtual Result<ResourceId> GetResourceId(const std::string& name) const = 0; + + ~TargetResourceContainer() override = default; +}; + +struct OverlayManifestInfo { + std::string package_name; // NOLINT(misc-non-private-member-variables-in-classes) + std::string name; // NOLINT(misc-non-private-member-variables-in-classes) + std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes) + std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes) + ResourceId resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +struct OverlayData { + struct ResourceIdValue { + // The overlay resource id. + ResourceId overlay_id; + + // Whether or not references to the overlay resource id should be rewritten to its corresponding + // target id during resource resolution. + bool rewrite_id; + }; + + struct Value { + std::string resource_name; + std::variant<ResourceIdValue, TargetValue> value; + }; + + struct InlineStringPoolData { + // The binary data of the android::ResStringPool string pool. + std::unique_ptr<uint8_t[]> data; + + // The length of the binary data. + uint32_t data_length; + + // The offset added to TargetValue#data_value (the index of the string in the inline string + // pool) in order to prevent the indices of the overlay resource table string pool from + // colliding with the inline string pool indices. + uint32_t string_pool_offset; + }; + + // The overlay's mapping of target resource name to overlaid value. Use a vector to enforce that + // the overlay pairs are inserted into the ResourceMapping in the specified ordered. + std::vector<Value> pairs; + + // If the overlay maps a target resource to a string literal (not a string resource), then the + // this field contains information about the string pool in which the string literal resides so it + // can be inlined into an idmap. + std::optional<InlineStringPoolData> string_pool_data; +}; + +struct OverlayResourceContainer : public ResourceContainer { + static Result<std::unique_ptr<OverlayResourceContainer>> FromPath(std::string path); + + WARN_UNUSED virtual Result<OverlayManifestInfo> FindOverlayInfo( + const std::string& name) const = 0; + WARN_UNUSED virtual Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const = 0; + + ~OverlayResourceContainer() override = default; +}; + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h index f66916ccb78c..5a0a384f75a3 100644 --- a/cmds/idmap2/include/idmap2/ResourceMapping.h +++ b/cmds/idmap2/include/idmap2/ResourceMapping.h @@ -17,30 +17,22 @@ #ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_ #define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_ +#include <androidfw/ApkAssets.h> + #include <map> #include <memory> #include <utility> -#include "androidfw/ApkAssets.h" +#include "idmap2/FabricatedOverlay.h" #include "idmap2/LogInfo.h" #include "idmap2/Policies.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/XmlParser.h" -using android::idmap2::utils::OverlayManifestInfo; - using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; namespace android::idmap2 { - -struct TargetValue { - typedef uint8_t DataType; - typedef uint32_t DataValue; - DataType data_type; - DataValue data_value; -}; - using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>; using OverlayResourceMap = std::map<ResourceId, ResourceId>; @@ -49,94 +41,60 @@ class ResourceMapping { // Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to // `false` disables all overlayable and policy enforcement: this is intended for backwards // compatibility pre-Q and unit tests. - static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable, LogInfo& log_info); + static Result<ResourceMapping> FromContainers(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, + const OverlayManifestInfo& overlay_info, + const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable, LogInfo& log_info); // Retrieves the mapping of target resource id to overlay value. - inline const TargetResourceMap& GetTargetToOverlayMap() const { - return target_map_; - } + WARN_UNUSED const TargetResourceMap& GetTargetToOverlayMap() const; // Retrieves the mapping of overlay resource id to target resource id. This allows a reference to // an overlay resource to appear as a reference to its corresponding target resource at runtime. - OverlayResourceMap GetOverlayToTargetMap() const; - - // Retrieves the build-time package id of the target package. - inline uint32_t GetTargetPackageId() const { - return target_package_id_; - } - - // Retrieves the build-time package id of the overlay package. - inline uint32_t GetOverlayPackageId() const { - return overlay_package_id_; - } + WARN_UNUSED const OverlayResourceMap& GetOverlayToTargetMap() const; // Retrieves the offset that was added to the index of inline string overlay values so the indices // do not collide with the indices of the overlay resource table string pool. - inline uint32_t GetStringPoolOffset() const { - return string_pool_offset_; - } + WARN_UNUSED uint32_t GetStringPoolOffset() const; // Retrieves the raw string pool data from the xml referenced in android:resourcesMap. - inline const StringPiece GetStringPoolData() const { - return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()), - string_pool_data_length_); - } + WARN_UNUSED StringPiece GetStringPoolData() const; private: ResourceMapping() = default; - // Maps a target resource id to an overlay resource id. - // If rewrite_overlay_reference is `true` then references to the overlay - // resource should appear as a reference to its corresponding target resource at runtime. - Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource, - bool rewrite_overlay_reference); - - // Maps a target resource id to a data type and value combination. - // The `data_type` is the runtime format of the data value (see Res_value::dataType). - Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type, - TargetValue::DataValue data_value); - - // Removes the overlay value mapping for the target resource. - void RemoveMapping(ResourceId target_resource); - - // Parses the mapping of target resources to overlay resources to generate a ResourceMapping. - static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - size_t string_pool_offset, - const XmlParser& overlay_parser, - LogInfo& log_info); - - // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay - // a target resource, a resource must exist in the overlay with the same type and entry name as - // the target resource. - static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am, - const AssetManager2* overlay_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - LogInfo& log_info); - - // Removes resources that do not pass policy or overlayable checks of the target package. - void FilterOverlayableResources(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, LogInfo& log_info); + // Maps a target resource id to an overlay resource id or a android::Res_value value. + // + // If `allow_rewriting_` is true, then the overlay-to-target map will be populated if the target + // resource id is mapped to an overlay resource id. + Result<Unit> AddMapping(ResourceId target_resource, + const std::variant<OverlayData::ResourceIdValue, TargetValue>& value); TargetResourceMap target_map_; - std::multimap<ResourceId, ResourceId> overlay_map_; - - uint32_t target_package_id_ = 0; - uint32_t overlay_package_id_ = 0; + OverlayResourceMap overlay_map_; uint32_t string_pool_offset_ = 0; uint32_t string_pool_data_length_ = 0; std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr; }; +inline const TargetResourceMap& ResourceMapping::GetTargetToOverlayMap() const { + return target_map_; +} + +inline const OverlayResourceMap& ResourceMapping::GetOverlayToTargetMap() const { + return overlay_map_; +} + +inline uint32_t ResourceMapping::GetStringPoolOffset() const { + return string_pool_offset_; +} + +inline StringPiece ResourceMapping::GetStringPoolData() const { + return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()), + string_pool_data_length_); +} + } // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_ diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index cd14d3e7254c..a0202dfee473 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -22,19 +22,26 @@ #include "androidfw/AssetManager2.h" #include "idmap2/Result.h" -#include "idmap2/ZipFile.h" namespace android::idmap2 { -// use typedefs to let the compiler warn us about implicit casts -typedef uint32_t ResourceId; // 0xpptteeee -typedef uint8_t PackageId; // pp in 0xpptteeee -typedef uint8_t TypeId; // tt in 0xpptteeee -typedef uint16_t EntryId; // eeee in 0xpptteeee - #define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16) #define EXTRACT_ENTRY(resid) (0x0000ffff & (resid)) +// use typedefs to let the compiler warn us about implicit casts +using ResourceId = uint32_t; // 0xpptteeee +using PackageId = uint8_t; // pp in 0xpptteeee +using TypeId = uint8_t; // tt in 0xpptteeee +using EntryId = uint16_t; // eeee in 0xpptteeee + +using DataType = uint8_t; // Res_value::dataType +using DataValue = uint32_t; // Res_value::data + +struct TargetValue { + DataType data_type; + DataValue data_value; +}; + namespace utils { // Returns whether the Res_value::data_type represents a dynamic or regular resource reference. @@ -43,20 +50,10 @@ bool IsReference(uint8_t data_type); // Converts the Res_value::data_type to a human-readable string representation. StringPiece DataTypeToString(uint8_t data_type); -struct OverlayManifestInfo { - std::string name; // NOLINT(misc-non-private-member-variables-in-classes) - std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes) - std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes) - uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes) -}; - -Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, - const std::string& name); - +// Retrieves the type and entry name of the resource in the AssetManager in the form type/entry. Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid); } // namespace utils - } // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_ diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h index 1c74ab3bb691..c968a5e6c04f 100644 --- a/cmds/idmap2/include/idmap2/XmlParser.h +++ b/cmds/idmap2/include/idmap2/XmlParser.h @@ -30,8 +30,7 @@ namespace android::idmap2 { -class XmlParser { - public: +struct XmlParser { using Event = ResXMLParser::event_code_t; class iterator; @@ -127,23 +126,19 @@ class XmlParser { }; // Creates a new xml parser beginning at the first tag. - static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size, - bool copy_data = false); - ~XmlParser(); + static Result<XmlParser> Create(const void* data, size_t size, bool copy_data = false); inline iterator tree_iterator() const { - return iterator(tree_); + return iterator(*tree_); } inline const ResStringPool& get_strings() const { - return tree_.getStrings(); + return tree_->getStrings(); } private: - XmlParser() = default; - mutable ResXMLTree tree_; - - DISALLOW_COPY_AND_ASSIGN(XmlParser); + explicit XmlParser(std::unique_ptr<ResXMLTree> tree); + mutable std::unique_ptr<ResXMLTree> tree_; }; } // namespace android::idmap2 diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h deleted file mode 100644 index 8f50e36256f6..000000000000 --- a/cmds/idmap2/include/idmap2/ZipFile.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_ -#define IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_ - -#include <memory> -#include <string> - -#include "android-base/macros.h" -#include "idmap2/Result.h" -#include "ziparchive/zip_archive.h" - -namespace android::idmap2 { - -struct MemoryChunk { - size_t size; - uint8_t buf[0]; - - static std::unique_ptr<MemoryChunk> Allocate(size_t size); - - private: - MemoryChunk() { - } -}; - -class ZipFile { - public: - static std::unique_ptr<const ZipFile> Open(const std::string& path); - - std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const; - Result<uint32_t> Crc(const std::string& entryPath) const; - - ~ZipFile(); - - private: - explicit ZipFile(const ::ZipArchiveHandle handle) : handle_(handle) { - } - - const ::ZipArchiveHandle handle_; - - DISALLOW_COPY_AND_ASSIGN(ZipFile); -}; - -} // namespace android::idmap2 - -#endif // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_ diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index c16310792d12..3bbe9d91deb6 100644 --- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp +++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp @@ -87,10 +87,6 @@ void BinaryStreamVisitor::visit(const IdmapData& data) { } void BinaryStreamVisitor::visit(const IdmapData::Header& header) { - Write8(header.GetTargetPackageId()); - Write8(header.GetOverlayPackageId()); - Write8(0U); // padding - Write8(0U); // padding Write32(header.GetTargetEntryCount()); Write32(header.GetTargetInlineEntryCount()); Write32(header.GetOverlayEntryCount()); diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp new file mode 100644 index 000000000000..4f61801aa594 --- /dev/null +++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "idmap2/FabricatedOverlay.h" + +#include <androidfw/ResourceUtils.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> +#include <utils/ByteOrder.h> +#include <zlib.h> + +#include <fstream> + +namespace android::idmap2 { + +namespace { +bool Read32(std::istream& stream, uint32_t* out) { + uint32_t value; + if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) { + *out = dtohl(value); + return true; + } + return false; +} + +void Write32(std::ostream& stream, uint32_t value) { + uint32_t x = htodl(value); + stream.write(reinterpret_cast<char*>(&x), sizeof(uint32_t)); +} +} // namespace + +FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay, + std::optional<uint32_t> crc_from_disk) + : overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), crc_from_disk_(crc_from_disk) { +} + +FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name, + const std::string& target_package_name) { + package_name_ = package_name; + name_ = name; + target_package_name_ = target_package_name; +} + +FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std::string& name) { + target_overlayable_ = name; + return *this; +} + +FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue( + const std::string& resource_name, uint8_t data_type, uint32_t data_value) { + entries_.emplace_back(Entry{resource_name, data_type, data_value}); + return *this; +} + +Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() { + std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> entries; + for (const auto& res_entry : entries_) { + StringPiece package_substr; + StringPiece type_name; + StringPiece entry_name; + if (!android::ExtractResourceName(StringPiece(res_entry.resource_name), &package_substr, + &type_name, &entry_name)) { + return Error("failed to parse resource name '%s'", res_entry.resource_name.c_str()); + } + + std::string package_name = + package_substr.empty() ? target_package_name_ : package_substr.to_string(); + if (type_name.empty()) { + return Error("resource name '%s' missing type name", res_entry.resource_name.c_str()); + } + + if (entry_name.empty()) { + 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>>())) + .first; + } + + auto type = package->second.find(type_name.to_string()); + if (type == package->second.end()) { + type = + package->second + .insert(std::make_pair<>(type_name.to_string(), std::map<std::string, TargetValue>())) + .first; + } + + auto entry = type->second.find(entry_name.to_string()); + if (entry == type->second.end()) { + entry = type->second.insert(std::make_pair<>(entry_name.to_string(), TargetValue())).first; + } + + entry->second = TargetValue{res_entry.data_type, res_entry.data_value}; + } + + pb::FabricatedOverlay overlay_pb; + overlay_pb.set_package_name(package_name_); + overlay_pb.set_name(name_); + overlay_pb.set_target_package_name(target_package_name_); + overlay_pb.set_target_overlayable(target_overlayable_); + + for (const auto& package : entries) { + auto package_pb = overlay_pb.add_packages(); + package_pb->set_name(package.first); + + for (const auto& type : package.second) { + auto type_pb = package_pb->add_types(); + type_pb->set_name(type.first); + + for (const 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); + } + } + } + + return FabricatedOverlay(std::move(overlay_pb)); +} + +Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) { + uint32_t magic; + if (!Read32(stream, &magic)) { + return Error("Failed to read fabricated overlay magic."); + } + + if (magic != kFabricatedOverlayMagic) { + return Error("Not a fabricated overlay file."); + } + + uint32_t version; + if (!Read32(stream, &version)) { + return Error("Failed to read fabricated overlay version."); + } + + if (version != 1) { + return Error("Invalid fabricated overlay version '%u'.", version); + } + + uint32_t crc; + if (!Read32(stream, &crc)) { + return Error("Failed to read fabricated overlay version."); + } + + pb::FabricatedOverlay overlay{}; + 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 + ? 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]); + + // Ensure serialization is deterministic + google::protobuf::io::ArrayOutputStream array_stream(data.get(), 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()) { + 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), + sizeof(uint32_t)); + crc = crc32(crc, data.get(), size); + data_ = SerializedData{std::move(data), size, crc}; + } + return &(*data_); +} +Result<uint32_t> FabricatedOverlay::GetCrc() const { + if (crc_from_disk_.has_value()) { + return *crc_from_disk_; + } + auto data = InitializeData(); + if (!data) { + return data.GetError(); + } + return (*data)->crc; +} + +Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const { + auto data = InitializeData(); + if (!data) { + return data.GetError(); + } + + Write32(stream, kFabricatedOverlayMagic); + Write32(stream, kFabricatedOverlayCurrentVersion); + Write32(stream, (*data)->crc); + stream.write(reinterpret_cast<const char*>((*data)->data.get()), (*data)->data_size); + if (stream.bad()) { + return Error("Failed to write serialized fabricated overlay."); + } + + return Unit{}; +} + +using FabContainer = FabricatedOverlayContainer; +FabContainer::FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path) + : overlay_(std::forward<FabricatedOverlay>(overlay)), path_(std::forward<std::string>(path)) { +} + +FabContainer::~FabricatedOverlayContainer() = default; + +Result<std::unique_ptr<FabContainer>> FabContainer::FromPath(std::string path) { + std::fstream fin(path); + auto overlay = FabricatedOverlay::FromBinaryStream(fin); + if (!overlay) { + return overlay.GetError(); + } + return std::unique_ptr<FabContainer>( + new FabricatedOverlayContainer(std::move(*overlay), std::move(path))); +} + +std::unique_ptr<FabricatedOverlayContainer> FabContainer::FromOverlay(FabricatedOverlay&& overlay) { + return std::unique_ptr<FabContainer>( + new FabricatedOverlayContainer(std::move(overlay), {} /* path */)); +} + +OverlayManifestInfo FabContainer::GetManifestInfo() const { + const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_; + return OverlayManifestInfo{ + .package_name = overlay_pb.package_name(), + .name = overlay_pb.name(), + .target_package = overlay_pb.target_package_name(), + .target_name = overlay_pb.target_overlayable(), + }; +} + +Result<OverlayManifestInfo> FabContainer::FindOverlayInfo(const std::string& name) const { + const OverlayManifestInfo info = GetManifestInfo(); + if (name != info.name) { + return Error("Failed to find name '%s' in fabricated overlay", name.c_str()); + } + return info; +} + +Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info) const { + const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_; + if (info.name != overlay_pb.name()) { + return Error("Failed to find name '%s' in fabricated overlay", info.name.c_str()); + } + + OverlayData result{}; + for (const auto& package : overlay_pb.packages()) { + for (const auto& type : package.types()) { + for (const auto& entry : type.entries()) { + auto name = base::StringPrintf("%s:%s/%s", package.name().c_str(), type.name().c_str(), + entry.name().c_str()); + const auto& res_value = entry.res_value(); + result.pairs.emplace_back(OverlayData::Value{ + name, TargetValue{.data_type = static_cast<uint8_t>(res_value.data_type()), + .data_value = res_value.data_value()}}); + } + } + } + return result; +} + +Result<uint32_t> FabContainer::GetCrc() const { + return overlay_.GetCrc(); +} + +const std::string& FabContainer::GetPath() const { + return path_; +} + +Result<std::string> FabContainer::GetResourceName(ResourceId /* id */) const { + return Error("Fabricated overlay does not contain resources."); +} + +} // namespace android::idmap2
\ No newline at end of file diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp index 3af1f70ebe39..98a4ceabdb94 100644 --- a/cmds/idmap2/libidmap2/FileUtils.cpp +++ b/cmds/idmap2/libidmap2/FileUtils.cpp @@ -47,4 +47,19 @@ bool UidHasWriteAccessToPath(uid_t uid ATTRIBUTE_UNUSED, const std::string& path } #endif +std::string RandomStringForPath(const size_t length) { + constexpr char kChars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + constexpr size_t kCharLastIndex = sizeof(kChars) - 1; + + std::string out_rand; + out_rand.reserve(length); + + std::random_device rd; + std::uniform_int_distribution<int> dist(0, kCharLastIndex); + for (size_t i = 0; i < length; i++) { + out_rand[i] = kChars[dist(rd) % (kCharLastIndex)]; + } + return out_rand; +} + } // namespace android::idmap2::utils diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index a0cc407fc948..6515d5516d83 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -20,23 +20,16 @@ #include <iostream> #include <iterator> #include <limits> -#include <map> #include <memory> -#include <set> #include <string> #include <utility> -#include <vector> #include "android-base/macros.h" -#include "android-base/stringprintf.h" #include "androidfw/AssetManager2.h" #include "idmap2/ResourceMapping.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/ZipFile.h" -#include "utils/String16.h" -#include "utils/String8.h" namespace android::idmap2 { @@ -93,22 +86,13 @@ bool WARN_UNUSED ReadString(std::istream& stream, std::string* out) { } // namespace -Result<uint32_t> GetPackageCrc(const ZipFile& zip) { - const Result<uint32_t> a = zip.Crc("resources.arsc"); - const Result<uint32_t> b = zip.Crc("AndroidManifest.xml"); - return a && b - ? Result<uint32_t>(*a ^ *b) - : Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc"); -} - std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader()); if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_)) { return nullptr; } - if (idmap_header->magic_ != kIdmapMagic || - idmap_header->version_ != kIdmapCurrentVersion) { + if (idmap_header->magic_ != kIdmapMagic || idmap_header->version_ != kIdmapCurrentVersion) { // Do not continue parsing if the file is not a current version idmap. return nullptr; } @@ -127,32 +111,22 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s return std::move(idmap_header); } -Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path, - const std::string& overlay_path, +Result<Unit> IdmapHeader::IsUpToDate(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, const std::string& overlay_name, PolicyBitmask fulfilled_policies, bool enforce_overlayable) const { - const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path); - if (!target_zip) { - return Error("failed to open target %s", target_path.c_str()); - } - - const Result<uint32_t> target_crc = GetPackageCrc(*target_zip); + const Result<uint32_t> target_crc = target.GetCrc(); if (!target_crc) { return Error("failed to get target crc"); } - const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path); - if (!overlay_zip) { - return Error("failed to overlay target %s", overlay_path.c_str()); - } - - const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip); + const Result<uint32_t> overlay_crc = overlay.GetCrc(); if (!overlay_crc) { return Error("failed to get overlay crc"); } - return IsUpToDate(target_path, overlay_path, overlay_name, *target_crc, *overlay_crc, + return IsUpToDate(target.GetPath(), overlay.GetPath(), overlay_name, *target_crc, *overlay_crc, fulfilled_policies, enforce_overlayable); } @@ -209,11 +183,7 @@ Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path, std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header()); - - uint8_t padding; - if (!Read8(stream, &idmap_data_header->target_package_id_) || - !Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) || - !Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) || + if (!Read32(stream, &idmap_data_header->target_entry_count) || !Read32(stream, &idmap_data_header->target_entry_inline_count) || !Read32(stream, &idmap_data_header->overlay_entry_count) || !Read32(stream, &idmap_data_header->string_pool_index_offset)) { @@ -321,8 +291,6 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping( } std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header()); - data_header->target_package_id_ = resource_mapping.GetTargetPackageId(); - data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId(); data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size()); data_header->target_entry_inline_count = static_cast<uint32_t>(data->target_inline_entries_.size()); @@ -332,57 +300,46 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping( return {std::move(data)}; } -Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const std::string& overlay_name, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable) { +Result<std::unique_ptr<const Idmap>> Idmap::FromContainers(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, + const std::string& overlay_name, + const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable) { SYSTRACE << "Idmap::FromApkAssets"; - const std::string& target_apk_path = target_apk_assets.GetPath(); - const std::string& overlay_apk_path = overlay_apk_assets.GetPath(); - - const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path); - if (!target_zip) { - return Error("failed to open target as zip"); - } - - const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path); - if (!overlay_zip) { - return Error("failed to open overlay as zip"); - } - std::unique_ptr<IdmapHeader> header(new IdmapHeader()); header->magic_ = kIdmapMagic; header->version_ = kIdmapCurrentVersion; - Result<uint32_t> crc = GetPackageCrc(*target_zip); - if (!crc) { - return Error(crc.GetError(), "failed to get zip CRC for target"); + const auto target_crc = target.GetCrc(); + if (!target_crc) { + return Error(target_crc.GetError(), "failed to get zip CRC for '%s'", target.GetPath().data()); } - header->target_crc_ = *crc; + header->target_crc_ = *target_crc; - crc = GetPackageCrc(*overlay_zip); - if (!crc) { - return Error(crc.GetError(), "failed to get zip CRC for overlay"); + const auto overlay_crc = overlay.GetCrc(); + if (!overlay_crc) { + return Error(overlay_crc.GetError(), "failed to get zip CRC for '%s'", + overlay.GetPath().data()); } - header->overlay_crc_ = *crc; + header->overlay_crc_ = *overlay_crc; + header->fulfilled_policies_ = fulfilled_policies; header->enforce_overlayable_ = enforce_overlayable; - header->target_path_ = target_apk_path; - header->overlay_path_ = overlay_apk_path; + header->target_path_ = target.GetPath(); + header->overlay_path_ = overlay.GetPath(); header->overlay_name_ = overlay_name; - auto info = utils::ExtractOverlayManifestInfo(overlay_apk_path, overlay_name); + auto info = overlay.FindOverlayInfo(overlay_name); if (!info) { - return info.GetError(); + return Error(info.GetError(), "failed to get overlay info for '%s'", overlay.GetPath().data()); } LogInfo log_info; - auto resource_mapping = - ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *info, - fulfilled_policies, enforce_overlayable, log_info); + auto resource_mapping = ResourceMapping::FromContainers( + target, overlay, *info, fulfilled_policies, enforce_overlayable, log_info); if (!resource_mapping) { - return resource_mapping.GetError(); + return Error(resource_mapping.GetError(), "failed to generate resource map for '%s'", + overlay.GetPath().data()); } auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping); diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index 7e090a983f95..721612cc567b 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -27,8 +27,6 @@ namespace android::idmap2 { -#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry)) - #define TAB " " void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { @@ -36,8 +34,8 @@ void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { void PrettyPrintVisitor::visit(const IdmapHeader& header) { stream_ << "Paths:" << std::endl - << TAB "target apk path : " << header.GetTargetPath() << std::endl - << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl; + << TAB "target path : " << header.GetTargetPath() << std::endl + << TAB "overlay path : " << header.GetOverlayPath() << std::endl; if (!header.GetOverlayName().empty()) { stream_ << "Overlay name: " << header.GetOverlayName() << std::endl; @@ -53,14 +51,11 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) { } } - if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath())) { - target_am_.SetApkAssets({target_apk_.get()}); - apk_assets_.push_back(std::move(target_apk_)); + if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) { + target_ = std::move(*target); } - - if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath())) { - overlay_am_.SetApkAssets({overlay_apk.get()}); - apk_assets_.push_back(std::move(overlay_apk)); + if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) { + overlay_ = std::move(*overlay); } stream_ << "Mapping:" << std::endl; @@ -72,23 +67,20 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) void PrettyPrintVisitor::visit(const IdmapData& data) { static constexpr const char* kUnknownResourceName = "???"; - const bool target_package_loaded = !target_am_.GetApkAssets().empty(); - const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty(); - const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size()); const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset(); for (const auto& target_entry : data.GetTargetEntries()) { std::string target_name = kUnknownResourceName; - if (target_package_loaded) { - if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) { + if (target_ != nullptr) { + if (auto name = target_->GetResourceName(target_entry.target_id)) { target_name = *name; } } std::string overlay_name = kUnknownResourceName; - if (overlay_package_loaded) { - if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) { + if (overlay_ != nullptr) { + if (auto name = overlay_->GetResourceName(target_entry.overlay_id)) { overlay_name = *name; } } @@ -112,8 +104,8 @@ void PrettyPrintVisitor::visit(const IdmapData& data) { } std::string target_name = kUnknownResourceName; - if (target_package_loaded) { - if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) { + if (target_ != nullptr) { + if (auto name = target_->GetResourceName(target_entry.target_id)) { target_name = *name; } } diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index b517aa3a0c01..a016a36a2e3d 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -18,16 +18,13 @@ #include <algorithm> #include <cstdarg> -#include <string> #include "android-base/macros.h" #include "android-base/stringprintf.h" -#include "androidfw/ApkAssets.h" #include "idmap2/PolicyUtils.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" -using android::ApkAssets; using android::idmap2::policy::PoliciesToDebugString; namespace android::idmap2 { @@ -48,27 +45,19 @@ void RawPrintVisitor::visit(const IdmapHeader& header) { print(header.GetOverlayName(), true /* print_value */, "overlay name"); print(header.GetDebugInfo(), false /* print_value */, "debug info"); - auto target_apk_ = ApkAssets::Load(header.GetTargetPath()); - if (target_apk_) { - target_am_.SetApkAssets({target_apk_.get()}); - apk_assets_.push_back(std::move(target_apk_)); + if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) { + target_ = std::move(*target); } - - auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath()); - if (overlay_apk_) { - overlay_am_.SetApkAssets({overlay_apk_.get()}); - apk_assets_.push_back(std::move(overlay_apk_)); + if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) { + overlay_ = std::move(*overlay); } } void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { - const bool target_package_loaded = !target_am_.GetApkAssets().empty(); - const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty(); - for (auto& target_entry : data.GetTargetEntries()) { Result<std::string> target_name(Error("")); - if (target_package_loaded) { - target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id); + if (target_ != nullptr) { + target_name = target_->GetResourceName(target_entry.target_id); } if (target_name) { print(target_entry.target_id, "target id: %s", target_name->c_str()); @@ -77,8 +66,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { } Result<std::string> overlay_name(Error("")); - if (overlay_package_loaded) { - overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id); + if (overlay_ != nullptr) { + overlay_name = overlay_->GetResourceName(target_entry.overlay_id); } if (overlay_name) { print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str()); @@ -89,8 +78,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { for (auto& target_entry : data.GetTargetInlineEntries()) { Result<std::string> target_name(Error("")); - if (target_package_loaded) { - target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id); + if (target_ != nullptr) { + target_name = target_->GetResourceName(target_entry.target_id); } if (target_name) { print(target_entry.target_id, "target id: %s", target_name->c_str()); @@ -104,10 +93,10 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { utils::DataTypeToString(target_entry.value.data_type).data()); Result<std::string> overlay_name(Error("")); - if (overlay_package_loaded && + if (overlay_ != nullptr && (target_entry.value.data_value == Res_value::TYPE_REFERENCE || target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) { - overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value); + overlay_name = overlay_->GetResourceName(target_entry.value.data_value); } if (overlay_name) { @@ -119,8 +108,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { for (auto& overlay_entry : data.GetOverlayEntries()) { Result<std::string> overlay_name(Error("")); - if (overlay_package_loaded) { - overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id); + if (overlay_ != nullptr) { + overlay_name = overlay_->GetResourceName(overlay_entry.overlay_id); } if (overlay_name) { @@ -130,8 +119,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { } Result<std::string> target_name(Error("")); - if (target_package_loaded) { - target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id); + if (target_ != nullptr) { + target_name = target_->GetResourceName(overlay_entry.target_id); } if (target_name) { @@ -145,9 +134,6 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { } void RawPrintVisitor::visit(const IdmapData::Header& header) { - print(header.GetTargetPackageId(), "target package id"); - print(header.GetOverlayPackageId(), "overlay package id"); - align(); print(header.GetTargetEntryCount(), "target entry count"); print(header.GetTargetInlineEntryCount(), "target inline entry count"); print(header.GetOverlayEntryCount(), "overlay entry count"); diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp new file mode 100644 index 000000000000..9147ccaaa17a --- /dev/null +++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "idmap2/ResourceContainer.h" + +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/Util.h" +#include "idmap2/FabricatedOverlay.h" +#include "idmap2/XmlParser.h" + +namespace android::idmap2 { +namespace { +#define REWRITE_PACKAGE(resid, package_id) \ + (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U)) + +#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24) + +constexpr ResourceId kAttrName = 0x01010003; +constexpr ResourceId kAttrResourcesMap = 0x01010609; +constexpr ResourceId kAttrTargetName = 0x0101044d; +constexpr ResourceId kAttrTargetPackage = 0x01010021; + +// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package +// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so +// this assumption tends to work out. That said, the correct thing to do is to scan +// resources.arsc for a package with a given name as read from the package manifest instead of +// relying on a hard-coded index. This however requires storing the package name in the idmap +// header, which in turn requires incrementing the idmap version. Because the initial version of +// idmap2 is compatible with idmap, this will have to wait for now. +const LoadedPackage* GetPackageAtIndex0(const LoadedArsc* loaded_arsc) { + const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); + if (packages.empty()) { + return nullptr; + } + return loaded_arsc->GetPackageById(packages[0]->GetPackageId()); +} + +Result<uint32_t> CalculateCrc(const ZipAssetsProvider* zip_assets) { + constexpr const char* kResourcesArsc = "resources.arsc"; + std::optional<uint32_t> res_crc = zip_assets->GetCrc(kResourcesArsc); + if (!res_crc) { + return Error("failed to get CRC for '%s'", kResourcesArsc); + } + + constexpr const char* kManifest = "AndroidManifest.xml"; + std::optional<uint32_t> man_crc = zip_assets->GetCrc(kManifest); + if (!man_crc) { + return Error("failed to get CRC for '%s'", kManifest); + } + + return *res_crc ^ *man_crc; +} + +Result<XmlParser> OpenXmlParser(const std::string& entry_path, const ZipAssetsProvider* zip) { + auto manifest = zip->Open(entry_path); + if (manifest == nullptr) { + return Error("failed to find %s ", entry_path.c_str()); + } + + auto size = manifest->getLength(); + auto buffer = manifest->getIncFsBuffer(true /* aligned */).convert<uint8_t>(); + if (!buffer.verify(size)) { + return Error("failed to read entire %s", entry_path.c_str()); + } + + return XmlParser::Create(buffer.unsafe_ptr(), size, true /* copyData */); +} + +Result<XmlParser> OpenXmlParser(ResourceId id, const ZipAssetsProvider* zip, + const AssetManager2* am) { + const auto ref_table = am->GetDynamicRefTableForCookie(0); + if (ref_table == nullptr) { + return Error("failed to find dynamic ref table for cookie 0"); + } + + ref_table->lookupResourceId(&id); + auto value = am->GetResource(id); + if (!value.has_value()) { + return Error("failed to find resource for id 0x%08x", id); + } + + if (value->type != Res_value::TYPE_STRING) { + return Error("resource for is 0x%08x is not a file", id); + } + + auto string_pool = am->GetStringPoolForCookie(value->cookie); + auto file = string_pool->string8ObjectAt(value->data); + if (!file.has_value()) { + return Error("failed to find string for index %d", value->data); + } + + return OpenXmlParser(file->c_str(), zip); +} + +Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const ZipAssetsProvider* zip, + const std::string& name) { + Result<XmlParser> xml = OpenXmlParser("AndroidManifest.xml", zip); + if (!xml) { + return xml.GetError(); + } + + auto manifest_it = xml->tree_iterator(); + if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") { + return Error("root element tag is not <manifest> in AndroidManifest.xml"); + } + + std::string package_name; + if (auto result_str = manifest_it->GetAttributeStringValue("package")) { + package_name = *result_str; + } else { + return result_str.GetError(); + } + + for (auto&& it : manifest_it) { + if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") { + continue; + } + + OverlayManifestInfo info{}; + info.package_name = package_name; + if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) { + if (*result_str != name) { + // A value for android:name was found, but either a the name does not match the requested + // name, or an <overlay> tag with no name was requested. + continue; + } + info.name = *result_str; + } else if (!name.empty()) { + // This tag does not have a value for android:name, but an <overlay> tag with a specific name + // has been requested. + continue; + } + + if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) { + info.target_package = *result_str; + } else { + return Error("android:targetPackage missing from <overlay> in AndroidManifest.xml"); + } + + if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) { + info.target_name = *result_str; + } + + if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) { + if (utils::IsReference((*result_value).dataType)) { + info.resource_mapping = (*result_value).data; + } else { + return Error("android:resourcesMap is not a reference in AndroidManifest.xml"); + } + } + return info; + } + + return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml", name.c_str()); +} + +Result<OverlayData> CreateResourceMapping(ResourceId id, const ZipAssetsProvider* zip, + const AssetManager2* overlay_am, + const LoadedArsc* overlay_arsc, + const LoadedPackage* overlay_package) { + auto parser = OpenXmlParser(id, zip, overlay_am); + if (!parser) { + return parser.GetError(); + } + + OverlayData overlay_data{}; + const uint32_t string_pool_offset = overlay_arsc->GetStringPool()->size(); + const uint8_t package_id = overlay_package->GetPackageId(); + auto root_it = parser->tree_iterator(); + if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") { + return Error("root element is not <overlay> tag"); + } + + auto overlay_it_end = root_it.end(); + for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) { + if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) { + return Error("failed to parse overlay xml document"); + } + + if (overlay_it->event() != XmlParser::Event::START_TAG) { + continue; + } + + if (overlay_it->name() != "item") { + return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str()); + } + + Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target"); + if (!target_resource) { + return Error(R"(<item> tag missing expected attribute "target")"); + } + + Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value"); + if (!overlay_resource) { + return Error(R"(<item> tag missing expected attribute "value")"); + } + + if (overlay_resource->dataType == Res_value::TYPE_STRING) { + overlay_resource->data += string_pool_offset; + } + + if (utils::IsReference(overlay_resource->dataType)) { + // Only rewrite resources defined within the overlay package to their corresponding target + // resource ids at runtime. + bool rewrite_id = package_id == EXTRACT_PACKAGE(overlay_resource->data); + overlay_data.pairs.emplace_back(OverlayData::Value{ + *target_resource, OverlayData::ResourceIdValue{overlay_resource->data, rewrite_id}}); + } else { + overlay_data.pairs.emplace_back( + OverlayData::Value{*target_resource, TargetValue{.data_type = overlay_resource->dataType, + .data_value = overlay_resource->data}}); + } + } + + const auto& string_pool = parser->get_strings(); + const uint32_t string_pool_data_length = string_pool.bytes(); + overlay_data.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 = string_pool_offset, + }; + + // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here. + memcpy(overlay_data.string_pool_data->data.get(), string_pool.data().unsafe_ptr(), + string_pool_data_length); + return overlay_data; +} + +OverlayData CreateResourceMappingLegacy(const AssetManager2* overlay_am, + const LoadedPackage* overlay_package) { + OverlayData overlay_data{}; + for (const ResourceId overlay_resid : *overlay_package) { + if (auto name = utils::ResToTypeEntryName(*overlay_am, overlay_resid)) { + // Disable rewriting. Overlays did not support internal references before + // android:resourcesMap. Do not introduce new behavior. + overlay_data.pairs.emplace_back(OverlayData::Value{ + *name, OverlayData::ResourceIdValue{overlay_resid, false /* rewrite_id */}}); + } + } + return overlay_data; +} + +struct ResState { + std::unique_ptr<ApkAssets> apk_assets; + const LoadedArsc* arsc; + const LoadedPackage* package; + std::unique_ptr<AssetManager2> am; + ZipAssetsProvider* zip_assets; + + static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip) { + ResState state; + state.zip_assets = zip.get(); + if ((state.apk_assets = ApkAssets::Load(std::move(zip))) == nullptr) { + return Error("failed to load apk asset"); + } + + if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) { + return Error("failed to retrieve loaded arsc"); + } + + if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) { + return Error("failed to retrieve loaded package at index 0"); + } + + state.am = std::make_unique<AssetManager2>(); + if (!state.am->SetApkAssets({state.apk_assets.get()})) { + return Error("failed to create asset manager"); + } + + return state; + } +}; + +} // namespace + +struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer { + static Result<std::unique_ptr<ApkResourceContainer>> FromPath(const std::string& path); + + // inherited from TargetResourceContainer + Result<bool> DefinesOverlayable() const override; + Result<const android::OverlayableInfo*> GetOverlayableInfo(ResourceId id) const override; + Result<ResourceId> GetResourceId(const std::string& name) const override; + + // inherited from OverlayResourceContainer + Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override; + Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override; + + // inherited from ResourceContainer + Result<uint32_t> GetCrc() const override; + Result<std::string> GetResourceName(ResourceId id) const override; + const std::string& GetPath() const override; + + ~ApkResourceContainer() override = default; + + private: + ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, std::string path); + + Result<const ResState*> GetState() const; + ZipAssetsProvider* GetZipAssets() const; + + mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_; + std::string path_; +}; + +ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, + std::string path) + : state_(std::move(zip_assets)), path_(std::move(path)) { +} + +Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath( + const std::string& path) { + auto zip_assets = ZipAssetsProvider::Create(path); + if (zip_assets == nullptr) { + return Error("failed to load zip assets"); + } + return std::unique_ptr<ApkResourceContainer>( + new ApkResourceContainer(std::move(zip_assets), path)); +} + +Result<const ResState*> ApkResourceContainer::GetState() const { + if (auto state = std::get_if<ResState>(&state_); state != nullptr) { + return state; + } + + auto state = + ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_))); + if (!state) { + return state.GetError(); + } + + state_ = std::move(*state); + return &std::get<ResState>(state_); +} + +ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const { + if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) { + return zip->get(); + } + return std::get<ResState>(state_).zip_assets; +} + +Result<bool> ApkResourceContainer::DefinesOverlayable() const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return (*state)->package->DefinesOverlayable(); +} + +Result<const android::OverlayableInfo*> ApkResourceContainer::GetOverlayableInfo( + ResourceId id) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return (*state)->package->GetOverlayableInfo(id); +} + +Result<OverlayManifestInfo> ApkResourceContainer::FindOverlayInfo(const std::string& name) const { + return ExtractOverlayManifestInfo(GetZipAssets(), name); +} + +Result<OverlayData> ApkResourceContainer::GetOverlayData(const OverlayManifestInfo& info) const { + const auto state = GetState(); + if (!state) { + return state.GetError(); + } + + if (info.resource_mapping != 0) { + return CreateResourceMapping(info.resource_mapping, GetZipAssets(), (*state)->am.get(), + (*state)->arsc, (*state)->package); + } + return CreateResourceMappingLegacy((*state)->am.get(), (*state)->package); +} + +Result<uint32_t> ApkResourceContainer::GetCrc() const { + return CalculateCrc(GetZipAssets()); +} + +const std::string& ApkResourceContainer::GetPath() const { + return path_; +} + +Result<ResourceId> ApkResourceContainer::GetResourceId(const std::string& name) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + auto id = (*state)->am->GetResourceId(name, "", (*state)->package->GetPackageName()); + if (!id.has_value()) { + return Error("failed to find resource '%s'", name.c_str()); + } + + // Retrieve the compile-time resource id of the target resource. + return REWRITE_PACKAGE(*id, (*state)->package->GetPackageId()); +} + +Result<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const { + auto state = GetState(); + if (!state) { + return state.GetError(); + } + return utils::ResToTypeEntryName(*(*state)->am, id); +} + +Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath( + std::string path) { + auto result = ApkResourceContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr<TargetResourceContainer>(result->release()); +} + +Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::FromPath( + std::string path) { + // Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay. + if (android::IsFabricatedOverlay(path)) { + auto result = FabricatedOverlayContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr<OverlayResourceContainer>(result->release()); + } + + // Fallback to loading the container as an APK. + auto result = ApkResourceContainer::FromPath(path); + if (!result) { + return result.GetError(); + } + return std::unique_ptr<OverlayResourceContainer>(result->release()); +} + +} // namespace android::idmap2
\ No newline at end of file diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index 46eeb8e6ac80..3bbbf248c87d 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -30,19 +30,12 @@ using android::base::StringPrintf; using android::idmap2::utils::BitmaskToPolicies; -using android::idmap2::utils::IsReference; -using android::idmap2::utils::ResToTypeEntryName; using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { namespace { - -#define REWRITE_PACKAGE(resid, package_id) \ - (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U)) -#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24) - std::string ConcatPolicies(const std::vector<std::string>& policies) { std::string message; for (const std::string& policy : policies) { @@ -55,11 +48,11 @@ std::string ConcatPolicies(const std::vector<std::string>& policies) { return message; } -Result<Unit> CheckOverlayable(const LoadedPackage& target_package, +Result<Unit> CheckOverlayable(const TargetResourceContainer& target, const OverlayManifestInfo& overlay_info, const PolicyBitmask& fulfilled_policies, const ResourceId& target_resource) { - static constexpr const PolicyBitmask sDefaultPolicies = + constexpr const PolicyBitmask kDefaultPolicies = PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE | PolicyFlags::CONFIG_SIGNATURE; @@ -68,8 +61,13 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package, // the overlay is preinstalled, signed with the same signature as the target or signed with the // same signature as reference package defined in SystemConfig under 'overlay-config-signature' // tag. - if (!target_package.DefinesOverlayable()) { - return (sDefaultPolicies & fulfilled_policies) != 0 + const Result<bool> defines_overlayable = target.DefinesOverlayable(); + if (!defines_overlayable) { + return Error(defines_overlayable.GetError(), "unable to retrieve overlayable info"); + } + + if (!*defines_overlayable) { + return (kDefaultPolicies & fulfilled_policies) != 0 ? Result<Unit>({}) : Error( "overlay must be preinstalled, signed with the same signature as the target," @@ -77,317 +75,92 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package, " <overlay-config-signature>."); } - const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource); - if (overlayable_info == nullptr) { + const auto overlayable_info = target.GetOverlayableInfo(target_resource); + if (!overlayable_info) { + return overlayable_info.GetError(); + } + + if (*overlayable_info == nullptr) { // Do not allow non-overlayable resources to be overlaid. return Error("target resource has no overlayable declaration"); } - if (overlay_info.target_name != overlayable_info->name) { + if (overlay_info.target_name != (*overlayable_info)->name) { // If the overlay supplies a target overlayable name, the resource must belong to the // overlayable defined with the specified name to be overlaid. return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")", - overlay_info.target_name.c_str(), overlayable_info->name.c_str()); + overlay_info.target_name.c_str(), (*overlayable_info)->name.c_str()); } // Enforce policy restrictions if the resource is declared as overlayable. - if ((overlayable_info->policy_flags & fulfilled_policies) == 0) { + if (((*overlayable_info)->policy_flags & fulfilled_policies) == 0) { return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")", ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(), - ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str()); + ConcatPolicies(BitmaskToPolicies((*overlayable_info)->policy_flags)).c_str()); } return Result<Unit>({}); } -// TODO(martenkongstad): scan for package name instead of assuming package at index 0 -// -// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package -// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so -// this assumption tends to work out. That said, the correct thing to do is to scan -// resources.arsc for a package with a given name as read from the package manifest instead of -// relying on a hard-coded index. This however requires storing the package name in the idmap -// header, which in turn requires incrementing the idmap version. Because the initial version of -// idmap2 is compatible with idmap, this will have to wait for now. -const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { - const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages(); - if (packages.empty()) { - return nullptr; - } - int id = packages[0]->GetPackageId(); - return loaded_arsc.GetPackageById(id); -} - -Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id, - const AssetManager2& asset_manager) { - auto value = asset_manager.GetResource(resource_id); - if (!value.has_value()) { - return Error("failed to find resource for id 0x%08x", resource_id); - } - - if (value->type != Res_value::TYPE_STRING) { - return Error("resource for is 0x%08x is not a file", resource_id); - } - - auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie); - auto file = string_pool->string8ObjectAt(value->data); - if (!file.has_value()) { - return Error("failed to find string for index %d", value->data); +std::string GetDebugResourceName(const ResourceContainer& container, ResourceId resid) { + auto name = container.GetResourceName(resid); + if (name) { + return *name; } - - // Load the overlay resource mappings from the file specified using android:resourcesMap. - auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER); - if (asset == nullptr) { - return Error("file \"%s\" not found", file->c_str()); - } - - return asset; + return StringPrintf("0x%08x", resid); } - } // namespace -Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - size_t string_pool_offset, - const XmlParser& overlay_parser, - LogInfo& log_info) { - ResourceMapping resource_mapping; - auto root_it = overlay_parser.tree_iterator(); - if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") { - return Error("root element is not <overlay> tag"); - } - - const uint8_t target_package_id = target_package->GetPackageId(); - const uint8_t overlay_package_id = overlay_package->GetPackageId(); - auto overlay_it_end = root_it.end(); - for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) { - if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) { - return Error("failed to parse overlay xml document"); - } - - if (overlay_it->event() != XmlParser::Event::START_TAG) { - continue; - } - - if (overlay_it->name() != "item") { - return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str()); - } - - Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target"); - if (!target_resource) { - return Error(R"(<item> tag missing expected attribute "target")"); - } - - Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value"); - if (!overlay_resource) { - return Error(R"(<item> tag missing expected attribute "value")"); - } - - auto target_id_result = - target_am->GetResourceId(*target_resource, "", target_package->GetPackageName()); - if (!target_id_result.has_value()) { - log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource - << "\" in target resources"); +Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceContainer& target, + const OverlayResourceContainer& overlay, + const OverlayManifestInfo& overlay_info, + const PolicyBitmask& fulfilled_policies, + bool enforce_overlayable, + LogInfo& log_info) { + auto overlay_data = overlay.GetOverlayData(overlay_info); + if (!overlay_data) { + return overlay_data.GetError(); + } + + ResourceMapping mapping; + for (const auto& overlay_pair : overlay_data->pairs) { + const auto target_resid = target.GetResourceId(overlay_pair.resource_name); + if (!target_resid) { + log_info.Warning(LogMessage() << target_resid.GetErrorMessage()); continue; } - // Retrieve the compile-time resource id of the target resource. - uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id); - - if (overlay_resource->dataType == Res_value::TYPE_STRING) { - overlay_resource->data += string_pool_offset; - } - - if (IsReference(overlay_resource->dataType)) { - // Only rewrite resources defined within the overlay package to their corresponding target - // resource ids at runtime. - bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data); - resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference); - } else { - resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data); - } - } - - return resource_mapping; -} - -Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy( - const AssetManager2* target_am, const AssetManager2* overlay_am, - const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) { - ResourceMapping resource_mapping; - const uint8_t target_package_id = target_package->GetPackageId(); - const auto end = overlay_package->end(); - for (auto iter = overlay_package->begin(); iter != end; ++iter) { - const ResourceId overlay_resid = *iter; - Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid); - if (!name) { - continue; + if (enforce_overlayable) { + // Filter out resources the overlay is not allowed to override. + auto overlayable = CheckOverlayable(target, overlay_info, fulfilled_policies, *target_resid); + if (!overlayable) { + log_info.Warning(LogMessage() << "overlay '" << overlay.GetPath() + << "' is not allowed to overlay resource '" + << GetDebugResourceName(target, *target_resid) + << "' in target: " << overlayable.GetErrorMessage()); + continue; + } } - // Find the resource with the same type and entry name within the target package. - const std::string full_name = - base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str()); - auto target_resource_result = target_am->GetResourceId(full_name); - if (!target_resource_result.has_value()) { - log_info.Warning(LogMessage() - << "failed to find resource \"" << full_name << "\" in target resources"); - continue; + if (auto result = mapping.AddMapping(*target_resid, overlay_pair.value); !result) { + return Error(result.GetError(), "failed to add mapping for '%s'", + GetDebugResourceName(target, *target_resid).c_str()); } - - // Retrieve the compile-time resource id of the target resource. - ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id); - resource_mapping.AddMapping(target_resource, overlay_resid, - false /* rewrite_overlay_reference */); } - return resource_mapping; -} - -void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am, - const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, - LogInfo& log_info) { - std::set<ResourceId> remove_ids; - for (const auto& target_map : target_map_) { - const ResourceId target_resid = target_map.first; - Result<Unit> success = - CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid); - if (success) { - continue; - } - - // Attempting to overlay a resource that is not allowed to be overlaid is treated as a - // warning. - Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid); - if (!name) { - name = StringPrintf("0x%08x", target_resid); - } - - log_info.Warning(LogMessage() << "overlay \"" << overlay_package->GetPackageName() - << "\" is not allowed to overlay resource \"" << *name - << "\" in target: " << success.GetErrorMessage()); - - remove_ids.insert(target_resid); + auto& string_pool_data = overlay_data->string_pool_data; + if (string_pool_data.has_value()) { + mapping.string_pool_offset_ = string_pool_data->string_pool_offset; + mapping.string_pool_data_ = std::move(string_pool_data->data); + mapping.string_pool_data_length_ = string_pool_data->data_length; } - for (const ResourceId target_resid : remove_ids) { - RemoveMapping(target_resid); - } + return std::move(mapping); } -Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets, - const ApkAssets& overlay_apk_assets, - const OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, - bool enforce_overlayable, - LogInfo& log_info) { - AssetManager2 target_asset_manager; - if (!target_asset_manager.SetApkAssets({&target_apk_assets})) { - return Error("failed to create target asset manager"); - } - - AssetManager2 overlay_asset_manager; - if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets})) { - return Error("failed to create overlay asset manager"); - } - - const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc(); - if (target_arsc == nullptr) { - return Error("failed to load target resources.arsc"); - } - - const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc(); - if (overlay_arsc == nullptr) { - return Error("failed to load overlay resources.arsc"); - } - - const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc); - if (target_pkg == nullptr) { - return Error("failed to load target package from resources.arsc"); - } - - const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc); - if (overlay_pkg == nullptr) { - return Error("failed to load overlay package from resources.arsc"); - } - - size_t string_pool_data_length = 0U; - size_t string_pool_offset = 0U; - std::unique_ptr<uint8_t[]> string_pool_data; - Result<ResourceMapping> resource_mapping = {{}}; - if (overlay_info.resource_mapping != 0U) { - // Use the dynamic reference table to find the assigned resource id of the map xml. - const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0); - uint32_t resource_mapping_id = overlay_info.resource_mapping; - ref_table->lookupResourceId(&resource_mapping_id); - - // Load the overlay resource mappings from the file specified using android:resourcesMap. - auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager); - if (!asset) { - return Error("failed opening xml for android:resourcesMap: %s", - asset.GetErrorMessage().c_str()); - } - - auto parser = - XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength()); - if (!parser) { - return Error("failed opening ResXMLTree"); - } - - // Copy the xml string pool data before the parse goes out of scope. - auto& string_pool = (*parser)->get_strings(); - string_pool_data_length = string_pool.bytes(); - string_pool_data.reset(new uint8_t[string_pool_data_length]); - - // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here. - memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length); - - // Offset string indices by the size of the overlay resource table string pool. - string_pool_offset = overlay_arsc->GetStringPool()->size(); - - resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg, - string_pool_offset, *(*parser), log_info); - } else { - // If no file is specified using android:resourcesMap, it is assumed that the overlay only - // defines resources intended to override target resources of the same type and name. - resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager, - target_pkg, overlay_pkg, log_info); - } - - if (!resource_mapping) { - return resource_mapping.GetError(); - } - - if (enforce_overlayable) { - // Filter out resources the overlay is not allowed to override. - (*resource_mapping) - .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info, - fulfilled_policies, log_info); - } - - resource_mapping->target_package_id_ = target_pkg->GetPackageId(); - resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId(); - resource_mapping->string_pool_offset_ = string_pool_offset; - resource_mapping->string_pool_data_ = std::move(string_pool_data); - resource_mapping->string_pool_data_length_ = string_pool_data_length; - return std::move(*resource_mapping); -} - -OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const { - // An overlay resource can override multiple target resources at once. Rewrite the overlay - // resource as the first target resource it overrides. - OverlayResourceMap map; - for (const auto& mappings : overlay_map_) { - map.insert(std::make_pair(mappings.first, mappings.second)); - } - return map; -} - -Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource, - bool rewrite_overlay_reference) { +Result<Unit> ResourceMapping::AddMapping( + ResourceId target_resource, + const std::variant<OverlayData::ResourceIdValue, TargetValue>& value) { if (target_map_.find(target_resource) != target_map_.end()) { return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); } @@ -395,49 +168,19 @@ Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the // runtime types are not compatible, it could cause runtime crashes when the resource is resolved. - target_map_.insert(std::make_pair(target_resource, overlay_resource)); - - if (rewrite_overlay_reference) { - overlay_map_.insert(std::make_pair(overlay_resource, target_resource)); - } - return Unit{}; -} - -Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, - TargetValue::DataType data_type, - TargetValue::DataValue data_value) { - if (target_map_.find(target_resource) != target_map_.end()) { - return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); + if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) { + target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id)); + if (overlay_resource->rewrite_id) { + // An overlay resource can override multiple target resources at once. Rewrite the overlay + // resource as the first target resource it overrides. + overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource)); + } + } else { + auto overlay_value = std::get<TargetValue>(value); + target_map_.insert(std::make_pair(target_resource, overlay_value)); } - // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the - // runtime types are not compatible, it could cause runtime crashes when the resource is resolved. - - target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value})); return Unit{}; } -void ResourceMapping::RemoveMapping(ResourceId target_resource) { - auto target_iter = target_map_.find(target_resource); - if (target_iter == target_map_.end()) { - return; - } - - const auto value = target_iter->second; - target_map_.erase(target_iter); - - const ResourceId* overlay_resource = std::get_if<ResourceId>(&value); - if (overlay_resource == nullptr) { - return; - } - - auto overlay_iter = overlay_map_.equal_range(*overlay_resource); - for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) { - if (i->second == target_resource) { - overlay_map_.erase(i); - return; - } - } -} - } // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index 4e85e5751300..e809bf1f4b02 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -17,27 +17,16 @@ #include "idmap2/ResourceUtils.h" #include <memory> -#include <string> #include "androidfw/StringPiece.h" #include "androidfw/Util.h" #include "idmap2/Result.h" -#include "idmap2/XmlParser.h" -#include "idmap2/ZipFile.h" using android::StringPiece16; using android::idmap2::Result; -using android::idmap2::XmlParser; -using android::idmap2::ZipFile; using android::util::Utf16ToUtf8; namespace android::idmap2::utils { -namespace { -constexpr ResourceId kAttrName = 0x01010003; -constexpr ResourceId kAttrResourcesMap = 0x01010609; -constexpr ResourceId kAttrTargetName = 0x0101044d; -constexpr ResourceId kAttrTargetPackage = 0x01010021; -} // namespace bool IsReference(uint8_t data_type) { return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE; @@ -97,71 +86,4 @@ Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) return out; } -Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, - const std::string& name) { - std::unique_ptr<const ZipFile> zip = ZipFile::Open(path); - if (!zip) { - return Error("failed to open %s as a zip file", path.c_str()); - } - - std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml"); - if (!entry) { - return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str()); - } - - Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size); - if (!xml) { - return Error("failed to parse AndroidManifest.xml from %s", path.c_str()); - } - - auto manifest_it = (*xml)->tree_iterator(); - if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") { - return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str()); - } - - for (auto&& it : manifest_it) { - if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") { - continue; - } - - OverlayManifestInfo info{}; - if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) { - if (*result_str != name) { - // A value for android:name was found, but either a the name does not match the requested - // name, or an <overlay> tag with no name was requested. - continue; - } - info.name = *result_str; - } else if (!name.empty()) { - // This tag does not have a value for android:name, but an <overlay> tag with a specific name - // has been requested. - continue; - } - - if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) { - info.target_package = *result_str; - } else { - return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(), - result_str.GetErrorMessage().c_str()); - } - - if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) { - info.target_name = *result_str; - } - - if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) { - if (IsReference((*result_value).dataType)) { - info.resource_mapping = (*result_value).data; - } else { - return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s", - path.c_str()); - } - } - return info; - } - - return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml of %s", - name.c_str(), path.c_str()); -} - } // namespace android::idmap2::utils diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp index 00baea46f909..70822c890288 100644 --- a/cmds/idmap2/libidmap2/XmlParser.cpp +++ b/cmds/idmap2/libidmap2/XmlParser.cpp @@ -151,16 +151,18 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& return value ? GetStringValue(parser_, *value, name) : value.GetError(); } -Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size, - bool copy_data) { - auto parser = std::unique_ptr<const XmlParser>(new XmlParser()); - if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) { +XmlParser::XmlParser(std::unique_ptr<ResXMLTree> tree) : tree_(std::move(tree)) { +} + +Result<XmlParser> XmlParser::Create(const void* data, size_t size, bool copy_data) { + auto tree = std::make_unique<ResXMLTree>(); + if (tree->setTo(data, size, copy_data) != NO_ERROR) { return Error("Malformed xml block"); } // Find the beginning of the first tag. XmlParser::Event event; - while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT && + while ((event = tree->next()) != XmlParser::Event::BAD_DOCUMENT && event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) { } @@ -172,11 +174,7 @@ Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, siz return Error("Bad xml document"); } - return parser; -} - -XmlParser::~XmlParser() { - tree_.uninit(); + return XmlParser{std::move(tree)}; } } // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp deleted file mode 100644 index 1e1a218163f0..000000000000 --- a/cmds/idmap2/libidmap2/ZipFile.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "idmap2/ZipFile.h" - -#include <memory> -#include <string> - -#include "idmap2/Result.h" - -namespace android::idmap2 { - -std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) { - void* ptr = ::operator new(sizeof(MemoryChunk) + size); - std::unique_ptr<MemoryChunk> chunk(reinterpret_cast<MemoryChunk*>(ptr)); - chunk->size = size; - return chunk; -} - -std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) { - ::ZipArchiveHandle handle; - int32_t status = ::OpenArchive(path.c_str(), &handle); - if (status != 0) { - ::CloseArchive(handle); - return nullptr; - } - return std::unique_ptr<ZipFile>(new ZipFile(handle)); -} - -ZipFile::~ZipFile() { - ::CloseArchive(handle_); -} - -std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryPath) const { - ::ZipEntry entry; - int32_t status = ::FindEntry(handle_, entryPath, &entry); - if (status != 0) { - return nullptr; - } - std::unique_ptr<MemoryChunk> chunk = MemoryChunk::Allocate(entry.uncompressed_length); - status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size); - if (status != 0) { - return nullptr; - } - return chunk; -} - -Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const { - ::ZipEntry entry; - int32_t status = ::FindEntry(handle_, entryPath, &entry); - if (status != 0) { - return Error("failed to find zip entry %s", entryPath.c_str()); - } - return entry.crc32; -} - -} // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/proto/fabricated_v1.proto b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto new file mode 100644 index 000000000000..a392b2b6d856 --- /dev/null +++ b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +package android.idmap2.pb; + +option optimize_for = LITE_RUNTIME; + +// All changes to the proto messages in this file MUST be backwards compatible. Backwards +// incompatible changes will cause previously fabricated overlays to be considered corrupt by the +// new proto message specification. +message FabricatedOverlay { + repeated ResourcePackage packages = 1; + string name = 2; + string package_name = 3; + string target_package_name = 4; + string target_overlayable = 5; +} + +message ResourcePackage { + string name = 1; + repeated ResourceType types = 2; +} + +message ResourceType { + string name = 1; + repeated ResourceEntry entries = 2; +} + +message ResourceEntry { + string name = 1; + oneof value { + ResourceValue res_value = 2; + } +} + +message ResourceValue { + // Corresponds with android::Res_value::dataType + uint32 data_type = 1; + // Corresponds with android::Res_value::data + uint32 data_value = 2; +}
\ No newline at end of file diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 524aabcec652..bf6332742787 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -33,7 +33,7 @@ using ::testing::NotNull; namespace android::idmap2 { TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); auto result1 = Idmap::FromBinaryStream(raw_stream); diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp new file mode 100644 index 000000000000..79ab2438af74 --- /dev/null +++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/file.h> +#include <gtest/gtest.h> +#include <idmap2/FabricatedOverlay.h> + +#include <fstream> + +namespace android::idmap2 { + +TEST(FabricatedOverlayTests, OverlayInfo) { + auto overlay = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetOverlayable("TestResources") + .Build(); + + ASSERT_TRUE(overlay); + auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay)); + auto info = container->FindOverlayInfo("SandTheme"); + ASSERT_TRUE(info); + EXPECT_EQ("SandTheme", (*info).name); + EXPECT_EQ("TestResources", (*info).target_name); + + info = container->FindOverlayInfo("OceanTheme"); + ASSERT_FALSE(info); +} + +TEST(FabricatedOverlayTests, SetResourceValue) { + auto overlay = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .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) + .Build(); + ASSERT_TRUE(overlay); + auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay)); + auto info = container->FindOverlayInfo("SandTheme"); + ASSERT_TRUE(info); + ASSERT_TRUE((*info).target_name.empty()); + + auto crc = (*container).GetCrc(); + ASSERT_TRUE(crc) << crc.GetErrorMessage(); + EXPECT_NE(0U, *crc); + + auto pairs = container->GetOverlayData(*info); + ASSERT_TRUE(pairs); + EXPECT_FALSE(pairs->string_pool_data.has_value()); + ASSERT_EQ(3U, pairs->pairs.size()); + + auto& it = pairs->pairs[0]; + ASSERT_EQ("com.example.target:integer/int1", it.resource_name); + auto entry = std::get_if<TargetValue>(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(1U, entry->data_value); + ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type); + + it = pairs->pairs[1]; + ASSERT_EQ("com.example.target:string/int3", it.resource_name); + entry = std::get_if<TargetValue>(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(0x7f010000, entry->data_value); + ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->data_type); + + it = pairs->pairs[2]; + ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name); + entry = std::get_if<TargetValue>(&it.value); + ASSERT_NE(nullptr, entry); + ASSERT_EQ(2U, entry->data_value); + ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type); +} + +TEST(FabricatedOverlayTests, SetResourceValueBadArgs) { + { + auto builder = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetResourceValue("int1", Res_value::TYPE_INT_DEC, 1U); + ASSERT_FALSE(builder.Build()); + } + { + auto builder = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetResourceValue("com.example.target:int2", Res_value::TYPE_INT_DEC, 1U); + ASSERT_FALSE(builder.Build()); + } +} + +TEST(FabricatedOverlayTests, SerializeAndDeserialize) { + auto overlay = + FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target") + .SetOverlayable("TestResources") + .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U) + .Build(); + ASSERT_TRUE(overlay); + TemporaryFile tf; + std::ofstream out(tf.path); + ASSERT_TRUE((*overlay).ToBinaryStream(out)); + out.close(); + + auto container = OverlayResourceContainer::FromPath(tf.path); + ASSERT_TRUE(container) << container.GetErrorMessage(); + EXPECT_EQ(tf.path, (*container)->GetPath()); + + auto crc = (*container)->GetCrc(); + ASSERT_TRUE(crc) << crc.GetErrorMessage(); + EXPECT_NE(0U, *crc); + + auto info = (*container)->FindOverlayInfo("SandTheme"); + ASSERT_TRUE(info) << info.GetErrorMessage(); + EXPECT_EQ("SandTheme", (*info).name); + EXPECT_EQ("TestResources", (*info).target_name); + + auto pairs = (*container)->GetOverlayData(*info); + ASSERT_TRUE(pairs) << pairs.GetErrorMessage(); + EXPECT_EQ(1U, pairs->pairs.size()); + + auto& it = pairs->pairs[0]; + ASSERT_EQ("com.example.target:integer/int1", it.resource_name); + auto entry = std::get_if<TargetValue>(&it.value); + ASSERT_NE(nullptr, entry); + EXPECT_EQ(1U, entry->data_value); + EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->data_type); +} + +} // namespace android::idmap2
\ No newline at end of file diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 16b68f01e8f5..9516ff83d718 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <android-base/file.h> + #include <cstdio> // fclose #include <fstream> #include <memory> @@ -61,12 +63,12 @@ TEST(IdmapTests, TestCanonicalIdmapPathFor) { } TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream stream(raw); std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); ASSERT_EQ(header->GetMagic(), 0x504d4449U); - ASSERT_EQ(header->GetVersion(), 0x07U); + ASSERT_EQ(header->GetVersion(), 0x08U); ASSERT_EQ(header->GetTargetCrc(), 0x1234U); ASSERT_EQ(header->GetOverlayCrc(), 0x5678U); ASSERT_EQ(header->GetFulfilledPolicies(), 0x11); @@ -81,7 +83,7 @@ TEST(IdmapTests, IdmapFailParsingDifferentVersion) { std::stringstream stream; stream << android::kIdmapMagic; stream << 0xffffffffU; - stream << std::string(kJunkSize, (char) 0xffU); + stream << std::string(kJunkSize, (char)0xffU); ASSERT_FALSE(Idmap::FromBinaryStream(stream)); } @@ -90,14 +92,13 @@ TEST(IdmapTests, IdmapFailParsingDifferentMagic) { std::stringstream stream; stream << 0xffffffffU; stream << android::kIdmapCurrentVersion; - stream << std::string(kJunkSize, (char) 0xffU); + stream << std::string(kJunkSize, (char)0xffU); ASSERT_FALSE(Idmap::FromBinaryStream(stream)); } TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { const size_t offset = kIdmapRawDataOffset; - std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), - kIdmapRawDataLen - offset); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset); std::istringstream stream(raw); std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream); @@ -108,8 +109,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { const size_t offset = kIdmapRawDataOffset; - std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), - kIdmapRawDataLen - offset); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset); std::istringstream stream(raw); std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream); @@ -134,7 +134,7 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { } TEST(IdmapTests, CreateIdmapFromBinaryStream) { - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream stream(raw); auto result = Idmap::FromBinaryStream(stream); @@ -143,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies); @@ -177,7 +177,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { } TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) { - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), 10); // data too small std::istringstream stream(raw); @@ -189,14 +189,14 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - auto idmap_result = Idmap::FromApkAssets( - *target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC, + auto idmap_result = Idmap::FromContainers( + **target, **overlay, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); auto& idmap = *idmap_result; @@ -204,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC); @@ -218,15 +218,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, - TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers( + **target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); auto& idmap = *idmap_result; ASSERT_THAT(idmap, NotNull()); @@ -255,25 +255,66 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) { ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4); } +TEST(IdmapTests, FabricatedOverlay) { + std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); + + auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target") + .SetOverlayable("TestResources") + .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U) + .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000) + .Build(); + + ASSERT_TRUE(frro); + TemporaryFile tf; + std::ofstream out(tf.path); + ASSERT_TRUE((*frro).ToBinaryStream(out)); + out.close(); + + auto overlay = OverlayResourceContainer::FromPath(tf.path); + ASSERT_TRUE(overlay); + + auto idmap_result = Idmap::FromContainers(**target, **overlay, "SandTheme", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); + ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); + auto& idmap = *idmap_result; + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + ASSERT_THAT(data, NotNull()); + ASSERT_EQ(data->GetTargetEntries().size(), 0U); + ASSERT_EQ(data->GetOverlayEntries().size(), 0U); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 2U); + 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); +} + TEST(IdmapTests, FailCreateIdmapInvalidName) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); { - auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers(**target, **overlay, "", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_FALSE(idmap_result); } { - auto idmap_result = - Idmap::FromApkAssets(*target_apk, *overlay_apk, "unknown", PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers(**target, **overlay, "unknown", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_FALSE(idmap_result); } } @@ -282,15 +323,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) { std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk"; - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, - TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto idmap_result = Idmap::FromContainers( + **target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage(); auto& idmap = *idmap_result; ASSERT_THAT(idmap, NotNull()); @@ -328,30 +369,29 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) { } Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets( - const std::string& local_target_apk_path, const std::string& local_overlay_apk_path, + const std::string& local_target_path, const std::string& local_overlay_path, const std::string& overlay_name, const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) { - auto overlay_info = - utils::ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name); - if (!overlay_info) { - return overlay_info.GetError(); + const std::string target_path(GetTestDataPath() + local_target_path); + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return Error(R"(Failed to load target "%s")", target_path.c_str()); } - const std::string target_apk_path(GetTestDataPath() + local_target_apk_path); - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error(R"(Failed to load target apk "%s")", target_apk_path.data()); + const std::string overlay_path(GetTestDataPath() + local_overlay_path); + auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return Error(R"(Failed to load overlay "%s")", overlay_path.c_str()); } - const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data()); + auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name); + if (!overlay_info) { + return Error(R"(Failed to find overlay name "%s")", overlay_name.c_str()); } LogInfo log_info; - auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info, - fulfilled_policies, enforce_overlayable, log_info); + auto mapping = ResourceMapping::FromContainers(**target, **overlay, *overlay_info, + fulfilled_policies, enforce_overlayable, log_info); if (!mapping) { return mapping.GetError(); } @@ -360,11 +400,9 @@ Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets( } TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) { - auto idmap_data = - TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", "DifferentPackages", - - PolicyFlags::PUBLIC, - /* enforce_overlayable */ false); + auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", + "DifferentPackages", PolicyFlags::PUBLIC, + /* enforce_overlayable */ false); ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); auto& data = *idmap_data; @@ -417,7 +455,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { const uint32_t target_crc = kIdmapRawDataTargetCrc; const uint32_t overlay_crc = kIdmapRawOverlayCrc; - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); auto result = Idmap::FromBinaryStream(raw_stream); @@ -468,8 +506,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(bad_target_crc_header, NotNull()); ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc()); ASSERT_FALSE(bad_target_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name, - target_crc, overlay_crc, policies, - /* enforce_overlayable */ true)); + target_crc, overlay_crc, policies, + /* enforce_overlayable */ true)); // overlay crc: bytes (0xc, 0xf) std::string bad_overlay_crc_string(stream.str()); @@ -483,8 +521,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(bad_overlay_crc_header, NotNull()); ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc()); ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name, - target_crc, overlay_crc, policies, - /* enforce_overlayable */ true)); + target_crc, overlay_crc, policies, + /* enforce_overlayable */ true)); // fulfilled policy: bytes (0x10, 0x13) std::string bad_policy_string(stream.str()); @@ -522,8 +560,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(bad_target_path_header, NotNull()); ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath()); ASSERT_FALSE(bad_target_path_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name, - target_crc, overlay_crc, policies, - /* enforce_overlayable */ true)); + target_crc, overlay_crc, policies, + /* enforce_overlayable */ true)); // overlay path: bytes (0x2c, 0x37) std::string bad_overlay_path_string(stream.str()); @@ -576,7 +614,7 @@ class TestVisitor : public Visitor { }; TEST(IdmapTests, TestVisitor) { - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream stream(raw); const auto idmap = Idmap::FromBinaryStream(stream); diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index 87ce0f13d19e..3d3d82a8c7dd 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -27,35 +27,31 @@ #include "idmap2/Idmap.h" #include "idmap2/PrettyPrintVisitor.h" -using android::ApkAssets; using android::base::StringPrintf; -using ::testing::NotNull; -using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk"); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, - TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, + PolicyFlags::PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(idmap); std::stringstream stream; PrettyPrintVisitor visitor(stream); (*idmap)->accept(&visitor); - ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); - ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); + ASSERT_NE(stream.str().find("target path : "), std::string::npos); + ASSERT_NE(stream.str().find("overlay path : "), std::string::npos); ASSERT_NE(stream.str().find(StringPrintf("0x%08x -> 0x%08x (integer/int1 -> integer/int1)\n", R::target::integer::int1, R::overlay::integer::int1)), std::string::npos); @@ -64,7 +60,7 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) { fclose(stderr); // silence expected warnings from libandroidfw - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); const auto idmap = Idmap::FromBinaryStream(raw_stream); @@ -74,8 +70,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) { PrettyPrintVisitor visitor(stream); (*idmap)->accept(&visitor); - ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); - ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); + ASSERT_NE(stream.str().find("target path : "), std::string::npos); + ASSERT_NE(stream.str().find("overlay path : "), std::string::npos); ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos); } diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 88f85efb0f84..a6371cb74f2e 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -30,17 +30,16 @@ #include "idmap2/RawPrintVisitor.h" using android::base::StringPrintf; -using ::testing::NotNull; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { -#define ASSERT_CONTAINS_REGEX(pattern, str) \ - do { \ - ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \ - << "pattern '" << pattern << "' not found in\n--------\n" \ - << str << "--------"; \ +#define ASSERT_CONTAINS_REGEX(pattern, str) \ + do { \ + ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \ + << "pattern '" << (pattern) << "' not found in\n--------\n" \ + << (str) << "--------"; \ } while (0) #define ADDRESS "[0-9a-f]{8}: " @@ -49,16 +48,15 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { fclose(stderr); // silence expected warnings const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - ASSERT_THAT(target_apk, NotNull()); + auto target = TargetResourceContainer::FromPath(target_apk_path); + ASSERT_TRUE(target); const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk"); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - ASSERT_THAT(overlay_apk, NotNull()); + auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); + ASSERT_TRUE(overlay); - const auto idmap = - Idmap::FromApkAssets(*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_DEFAULT, - PolicyFlags::PUBLIC, /* enforce_overlayable */ true); + const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, + PolicyFlags::PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(idmap); std::stringstream stream; @@ -66,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str()); ASSERT_CONTAINS_REGEX( StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING), stream.str()); @@ -75,8 +73,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str()); @@ -104,7 +100,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { fclose(stderr); // silence expected warnings from libandroidfw - std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen); + std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen); std::istringstream raw_stream(raw); const auto idmap = Idmap::FromBinaryStream(raw_stream); @@ -115,7 +111,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str()); @@ -126,8 +122,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay path: overlayX.apk\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "0000000b overlay name size\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str()); @@ -140,7 +134,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str()); - ASSERT_CONTAINS_REGEX("000000a8: ........ string pool\n", stream.str()); + ASSERT_CONTAINS_REGEX("000000a4: ........ string pool\n", stream.str()); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 0362529c4f3b..5a1d808af06f 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#include <android-base/file.h> +#include <androidfw/ResourceTypes.h> +#include <gtest/gtest.h> + #include <cstdio> // fclose #include <fstream> #include <memory> @@ -22,14 +26,10 @@ #include "R.h" #include "TestConstants.h" #include "TestHelpers.h" -#include "androidfw/ResourceTypes.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include "idmap2/LogInfo.h" #include "idmap2/ResourceMapping.h" using android::Res_value; -using android::idmap2::utils::ExtractOverlayManifestInfo; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; @@ -41,32 +41,36 @@ namespace android::idmap2 { ASSERT_TRUE(result) << result.GetErrorMessage(); \ } while (0) -Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_apk_path, - const std::string& local_overlay_apk_path, +Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_path, + const std::string& local_overlay_path, const std::string& overlay_name, const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) { - auto overlay_info = - ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name); - if (!overlay_info) { - return overlay_info.GetError(); + const std::string target_path = (local_target_path[0] == '/') + ? local_target_path + : (GetTestDataPath() + "/" + local_target_path); + auto target = TargetResourceContainer::FromPath(target_path); + if (!target) { + return Error(target.GetError(), R"(Failed to load target "%s")", target_path.c_str()); } - const std::string target_apk_path(GetTestDataPath() + local_target_apk_path); - std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); - if (!target_apk) { - return Error(R"(Failed to load target apk "%s")", target_apk_path.data()); + const std::string overlay_path = (local_overlay_path[0] == '/') + ? local_overlay_path + : (GetTestDataPath() + "/" + local_overlay_path); + auto overlay = OverlayResourceContainer::FromPath(overlay_path); + if (!overlay) { + return Error(overlay.GetError(), R"(Failed to load overlay "%s")", overlay_path.c_str()); } - const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path); - std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); - if (!overlay_apk) { - return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data()); + auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name); + if (!overlay_info) { + return Error(overlay_info.GetError(), R"(Failed to find overlay name "%s")", + overlay_name.c_str()); } LogInfo log_info; - return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info, - fulfilled_policies, enforce_overlayable, log_info); + return ResourceMapping::FromContainers(**target, **overlay, *overlay_info, fulfilled_policies, + enforce_overlayable, log_info); } Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource, @@ -128,7 +132,7 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& tar } TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); ASSERT_TRUE(resources) << resources.GetErrorMessage(); @@ -145,7 +149,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) { } TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "SwapNames", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "SwapNames", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -161,7 +165,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) { } TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "DifferentPackages", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -176,7 +180,7 @@ TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) { } TEST(ResourceMappingTests, InlineResources) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "Inline", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "Inline", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); constexpr size_t overlay_string_pool_size = 10U; @@ -189,8 +193,32 @@ TEST(ResourceMappingTests, InlineResources) { ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U)); } +TEST(ResourceMappingTests, FabricatedOverlay) { + auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target") + .SetOverlayable("TestResources") + .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U) + .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000) + .Build(); + + ASSERT_TRUE(frro); + TemporaryFile tf; + std::ofstream out(tf.path); + ASSERT_TRUE((*frro).ToBinaryStream(out)); + out.close(); + + auto resources = TestGetResourceMapping("target/target.apk", tf.path, "SandTheme", + PolicyFlags::PUBLIC, /* enforce_overlayable */ false); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U); + 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::integer::int1, Res_value::TYPE_INT_DEC, 2U)); +} + TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC, /* enforce_overlayable */ true); @@ -209,7 +237,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { // Resources that are not declared as overlayable and resources that a protected by policies the // overlay does not fulfill must not map to overlay resources. TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC, /* enforce_overlayable */ true); @@ -229,7 +257,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { // overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned // off. TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -264,7 +292,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore // Overlays that do not target an <overlayable> tag can overlay any resource if overlayable // enforcement is disabled. TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) { - auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "", + auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "", PolicyFlags::PUBLIC, /* enforce_overlayable */ false); @@ -284,10 +312,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget // Overlays that are neither pre-installed nor signed with the same signature as the target cannot // overlay packages that have not defined overlayable resources. TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) { - auto resources = - TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk", - "NoTargetName", PolicyFlags::PUBLIC, - /* enforce_overlayable */ true); + auto resources = TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk", + "NoTargetName", PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U); @@ -297,9 +324,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) { // signed with the same signature as the reference package can overlay packages that have not // defined overlayable resources. TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) { - auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void { + auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) { auto resources = - TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk", + TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk", TestConstants::OVERLAY_NAME_ALL_POLICIES, fulfilled_policies, /* enforce_overlayable */ true); diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp index 1f6bf49f5f0e..69142086765c 100644 --- a/cmds/idmap2/tests/ResourceUtilsTests.cpp +++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp @@ -17,10 +17,12 @@ #include <memory> #include <string> +#include "R.h" #include "TestHelpers.h" #include "androidfw/ApkAssets.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "idmap2/ResourceContainer.h" #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" @@ -49,8 +51,8 @@ class ResourceUtilsTests : public Idmap2Tests { }; TEST_F(ResourceUtilsTests, ResToTypeEntryName) { - Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U); - ASSERT_TRUE(name); + Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), R::target::integer::int1); + ASSERT_TRUE(name) << name.GetErrorMessage(); ASSERT_EQ(*name, "integer/int1"); } @@ -60,25 +62,34 @@ TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) { } TEST_F(ResourceUtilsTests, InvalidValidOverlayNameInvalidAttributes) { - auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk", - "InvalidName"); + auto overlay = + OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk"); + ASSERT_TRUE(overlay); + + auto info = (*overlay)->FindOverlayInfo("InvalidName"); ASSERT_FALSE(info); } TEST_F(ResourceUtilsTests, ValidOverlayNameInvalidAttributes) { - auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk", - "ValidName"); + auto overlay = + OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk"); + ASSERT_TRUE(overlay); + + auto info = (*overlay)->FindOverlayInfo("ValidName"); ASSERT_FALSE(info); } TEST_F(ResourceUtilsTests, ValidOverlayNameAndTargetPackageInvalidAttributes) { - auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk", - "ValidNameAndTargetPackage"); + auto overlay = + OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk"); + ASSERT_TRUE(overlay); + + auto info = (*overlay)->FindOverlayInfo("ValidNameAndTargetPackage"); ASSERT_TRUE(info); ASSERT_EQ("ValidNameAndTargetPackage", info->name); ASSERT_EQ("Valid", info->target_package); - ASSERT_EQ("", info->target_name); // Attribute resource id could not be found - ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found + ASSERT_EQ("", info->target_name); // Attribute resource id could not be found + ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found } -}// namespace android::idmap2 +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index 842af3dd7b3c..6b5f3a8a98eb 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -24,13 +24,13 @@ namespace android::idmap2 { -const unsigned char idmap_raw_data[] = { +const unsigned char kIdmapRawData[] = { // IDMAP HEADER // 0x0: magic 0x49, 0x44, 0x4d, 0x50, // 0x4: version - 0x07, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, // 0x8: target crc 0x34, 0x12, 0x00, 0x00, @@ -70,81 +70,72 @@ const unsigned char idmap_raw_data[] = { 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00, // DATA HEADER - // 0x54: target_package_id - 0x7f, - - // 0x55: overlay_package_id - 0x7f, - - // 0x56: padding - 0x00, 0x00, - - // 0x58: target_entry_count + // 0x54: target_entry_count 0x03, 0x00, 0x00, 0x00, - // 0x5c: target_inline_entry_count + // 0x58: target_inline_entry_count 0x01, 0x00, 0x00, 0x00, - // 0x60: overlay_entry_count + // 0x5c: overlay_entry_count 0x03, 0x00, 0x00, 0x00, - // 0x64: string_pool_offset + // 0x60: string_pool_offset 0x00, 0x00, 0x00, 0x00, // TARGET ENTRIES - // 0x68: target id (0x7f020000) + // 0x64: target id (0x7f020000) 0x00, 0x00, 0x02, 0x7f, - // 0x6c: overlay_id (0x7f020000) + // 0x68: overlay_id (0x7f020000) 0x00, 0x00, 0x02, 0x7f, - // 0x70: target id (0x7f030000) + // 0x6c: target id (0x7f030000) 0x00, 0x00, 0x03, 0x7f, - // 0x74: overlay_id (0x7f030000) + // 0x70: overlay_id (0x7f030000) 0x00, 0x00, 0x03, 0x7f, - // 0x78: target id (0x7f030002) + // 0x74: target id (0x7f030002) 0x02, 0x00, 0x03, 0x7f, - // 0x7c: overlay_id (0x7f030001) + // 0x78: overlay_id (0x7f030001) 0x01, 0x00, 0x03, 0x7f, // INLINE TARGET ENTRIES - // 0x80: target_id + // 0x7c: target_id 0x00, 0x00, 0x04, 0x7f, - // 0x84: Res_value::size (value ignored by idmap) + // 0x80: Res_value::size (value ignored by idmap) 0x08, 0x00, - // 0x87: Res_value::res0 (value ignored by idmap) + // 0x82: Res_value::res0 (value ignored by idmap) 0x00, - // 0x88: Res_value::dataType (TYPE_INT_HEX) + // 0x83: Res_value::dataType (TYPE_INT_HEX) 0x11, - // 0x8c: Res_value::data + // 0x84: Res_value::data 0x78, 0x56, 0x34, 0x12, // OVERLAY ENTRIES - // 0x90: 0x7f020000 -> 0x7f020000 + // 0x88: 0x7f020000 -> 0x7f020000 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, - // 0x98: 0x7f030000 -> 0x7f030000 + // 0x90: 0x7f030000 -> 0x7f030000 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, - // 0xa0: 0x7f030001 -> 0x7f030002 + // 0x98: 0x7f030001 -> 0x7f030002 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f, - // 0xa4: string pool + // 0xa0: string pool // string length, 0x04, 0x00, 0x00, 0x00, - // 0xa8 string contents "test" + // 0xa4 string contents "test" 0x74, 0x65, 0x73, 0x74}; -const unsigned int kIdmapRawDataLen = 0xac; +const unsigned int kIdmapRawDataLen = 0xa8; const unsigned int kIdmapRawDataOffset = 0x54; const unsigned int kIdmapRawDataTargetCrc = 0x1234; const unsigned int kIdmapRawOverlayCrc = 0x5678; diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp index 1a7eaca4d67b..eaf10a7d9282 100644 --- a/cmds/idmap2/tests/XmlParserTests.cpp +++ b/cmds/idmap2/tests/XmlParserTests.cpp @@ -19,25 +19,25 @@ #include <string> #include "TestHelpers.h" -#include "gmock/gmock.h" +#include "androidfw/AssetsProvider.h" #include "gtest/gtest.h" #include "idmap2/XmlParser.h" -#include "idmap2/ZipFile.h" namespace android::idmap2 { -Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); +Result<XmlParser> CreateTestParser(const std::string& test_file) { + auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk"); if (zip == nullptr) { return Error("Failed to open zip file"); } - auto data = zip->Uncompress(test_file); + auto data = zip->Open(test_file); if (data == nullptr) { return Error("Failed to open xml file"); } - return XmlParser::Create(data->buf, data->size, /* copy_data */ true); + return XmlParser::Create(data->getBuffer(true /* aligned*/), data->getLength(), + /* copy_data */ true); } TEST(XmlParserTests, Create) { @@ -54,7 +54,7 @@ TEST(XmlParserTests, NextChild) { auto xml = CreateTestParser("res/xml/test.xml"); ASSERT_TRUE(xml) << xml.GetErrorMessage(); - auto root_iter = (*xml)->tree_iterator(); + auto root_iter = xml->tree_iterator(); ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG); ASSERT_EQ(root_iter->name(), "a"); @@ -85,7 +85,7 @@ TEST(XmlParserTests, AttributeValues) { ASSERT_TRUE(xml) << xml.GetErrorMessage(); // Start at the <a> tag. - auto root_iter = (*xml)->tree_iterator(); + auto root_iter = xml->tree_iterator(); // Start at the <b> tag. auto a_iter = root_iter.begin(); @@ -111,8 +111,8 @@ TEST(XmlParserTests, IteratorEquality) { ASSERT_TRUE(xml) << xml.GetErrorMessage(); // Start at the <a> tag. - auto root_iter_1 = (*xml)->tree_iterator(); - auto root_iter_2 = (*xml)->tree_iterator(); + auto root_iter_1 = xml->tree_iterator(); + auto root_iter_2 = xml->tree_iterator(); ASSERT_EQ(root_iter_1, root_iter_2); ASSERT_EQ(*root_iter_1, *root_iter_2); @@ -146,7 +146,7 @@ TEST(XmlParserTests, Backtracking) { ASSERT_TRUE(xml) << xml.GetErrorMessage(); // Start at the <a> tag. - auto root_iter_1 = (*xml)->tree_iterator(); + auto root_iter_1 = xml->tree_iterator(); // Start at the <b> tag. auto a_iter_1 = root_iter_1.begin(); diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp deleted file mode 100644 index 3fca43621945..000000000000 --- a/cmds/idmap2/tests/ZipFileTests.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <cstdio> // fclose -#include <string> - -#include "TestHelpers.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "idmap2/Result.h" -#include "idmap2/ZipFile.h" - -using ::testing::IsNull; -using ::testing::NotNull; - -namespace android::idmap2 { - -TEST(ZipFileTests, BasicOpen) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - fclose(stderr); // silence expected warnings from libziparchive - auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist"); - ASSERT_THAT(fail, IsNull()); -} - -TEST(ZipFileTests, Crc) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - Result<uint32_t> crc = zip->Crc("AndroidManifest.xml"); - ASSERT_TRUE(crc); - ASSERT_EQ(*crc, 0x762f3d24); - - Result<uint32_t> crc2 = zip->Crc("does-not-exist"); - ASSERT_FALSE(crc2); -} - -TEST(ZipFileTests, Uncompress) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - auto data = zip->Uncompress("assets/lorem-ipsum.txt"); - ASSERT_THAT(data, NotNull()); - const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n"); - ASSERT_THAT(data->size, lorem_ipsum.size()); - ASSERT_THAT(std::string(reinterpret_cast<const char*>(data->buf), data->size), lorem_ipsum); - - auto fail = zip->Uncompress("does-not-exist"); - ASSERT_THAT(fail, IsNull()); -} - -} // namespace android::idmap2 diff --git a/core/api/current.txt b/core/api/current.txt index f82e00035e65..f627f98c34e3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -573,6 +573,7 @@ package android { field public static final int dropDownWidth = 16843362; // 0x1010262 field public static final int duplicateParentState = 16842985; // 0x10100e9 field public static final int duration = 16843160; // 0x1010198 + field public static final int edgeEffectType = 16844329; // 0x1010629 field public static final int editTextBackground = 16843602; // 0x1010352 field public static final int editTextColor = 16843601; // 0x1010351 field public static final int editTextPreferenceStyle = 16842898; // 0x1010092 @@ -848,6 +849,7 @@ package android { field public static final int keyboardNavigationCluster = 16844096; // 0x1010540 field public static final int keycode = 16842949; // 0x10100c5 field public static final int killAfterRestore = 16843420; // 0x101029c + field public static final int knownCerts = 16844330; // 0x101062a field public static final int label = 16842753; // 0x1010001 field public static final int labelFor = 16843718; // 0x10103c6 field @Deprecated public static final int labelTextSize = 16843317; // 0x1010235 @@ -10979,6 +10981,7 @@ package android.content { field public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS"; field public static final String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME"; field public static final String EXTRA_INTENT = "android.intent.extra.INTENT"; + field public static final String EXTRA_IS_BUBBLED = "android.intent.extra.IS_BUBBLED"; field public static final String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT"; field public static final String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY"; field public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID"; @@ -23032,10 +23035,12 @@ package android.media { method @Deprecated public int getStreamType(); method public String getTitle(android.content.Context); method public float getVolume(); + method public boolean isHapticGeneratorEnabled(); method public boolean isLooping(); method public boolean isPlaying(); method public void play(); method public void setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException; + method public boolean setHapticGeneratorEnabled(boolean); method public void setLooping(boolean); method @Deprecated public void setStreamType(int); method public void setVolume(float); @@ -53586,6 +53591,7 @@ package android.widget { method @Nullable public android.graphics.BlendMode getBlendMode(); method @ColorInt public int getColor(); method public int getMaxHeight(); + method public int getType(); method public boolean isFinished(); method public void onAbsorb(int); method public void onPull(float); @@ -53594,7 +53600,10 @@ package android.widget { method public void setBlendMode(@Nullable android.graphics.BlendMode); method public void setColor(@ColorInt int); method public void setSize(int, int); + method public void setType(int); field public static final android.graphics.BlendMode DEFAULT_BLEND_MODE; + field public static final int TYPE_GLOW = 0; // 0x0 + field public static final int TYPE_STRETCH = 1; // 0x1 } public class EditText extends android.widget.TextView { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 74ccbb14f816..fdd1e6660697 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -179,6 +179,7 @@ package android { field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD"; field public static final String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS"; field public static final String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING"; + field public static final String PERFORM_IMS_SINGLE_REGISTRATION = "android.permission.PERFORM_IMS_SINGLE_REGISTRATION"; field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION"; field public static final String POWER_SAVER = "android.permission.POWER_SAVER"; field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"; @@ -338,9 +339,8 @@ package android { field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 - field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a + field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemGallery = 17039399; // 0x1040027 - field public static final int config_systemVideoCall = 17039401; // 0x1040029 } public static final class R.style { @@ -2632,6 +2632,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000 field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000 + field public static final int PROTECTION_FLAG_KNOWN_SIGNER = 134217728; // 0x8000000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000 field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000 @@ -7044,11 +7045,15 @@ package android.net { method public long getExpiryTimeMillis(); method public long getRefreshTimeMillis(); method @Nullable public android.net.Uri getUserPortalUrl(); + method public int getUserPortalUrlSource(); method @Nullable public String getVenueFriendlyName(); method @Nullable public android.net.Uri getVenueInfoUrl(); + method public int getVenueInfoUrlSource(); method public boolean isCaptive(); method public boolean isSessionExtendable(); method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; } @@ -7062,8 +7067,10 @@ package android.net { method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); } public class ConnectivityManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e39b2b856661..06110319cb35 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -14,6 +14,7 @@ package android { field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; + field public static final String KEEP_UNINSTALLED_PACKAGES = "android.permission.KEEP_UNINSTALLED_PACKAGES"; field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS"; field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES"; @@ -53,9 +54,8 @@ package android { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 - field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a + field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemGallery = 17039399; // 0x1040027 - field public static final int config_systemVideoCall = 17039401; // 0x1040029 } } @@ -472,6 +472,7 @@ package android.app.admin { method @NonNull public String getOwnerName(); method @Nullable public String getTimeZone(); method public boolean isLeaveAllSystemAppsEnabled(); + method public void logParams(@NonNull String); method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int); field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR; } @@ -495,6 +496,7 @@ package android.app.admin { method public boolean isKeepAccountMigrated(); method public boolean isLeaveAllSystemAppsEnabled(); method public boolean isOrganizationOwnedProvisioning(); + method public void logParams(@NonNull String); method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int); field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR; } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 43d0269beae2..e2426d116319 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -50,7 +50,9 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.Icon; +import android.hardware.HardwareBuffer; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; @@ -76,6 +78,7 @@ import android.util.Singleton; import android.util.Size; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; +import android.window.TaskSnapshot; import com.android.internal.app.LocalePicker; import com.android.internal.app.procstats.ProcessStats; @@ -1740,6 +1743,62 @@ public class ActivityManager { */ public static class RecentTaskInfo extends TaskInfo implements Parcelable { /** + * @hide + */ + public static class PersistedTaskSnapshotData { + /** + * The bounds of the task when the last snapshot was taken, may be null if the task is + * not yet attached to the hierarchy. + * @see {@link android.window.TaskSnapshot#mTaskSize}. + * @hide + */ + public @Nullable Point taskSize; + + /** + * The content insets of the task when the task snapshot was taken. + * @see {@link android.window.TaskSnapshot#mContentInsets}. + * @hide + */ + public @Nullable Rect contentInsets; + + /** + * The size of the last snapshot taken, may be null if there is no associated snapshot. + * @see {@link android.window.TaskSnapshot#mSnapshot}. + * @hide + */ + public @Nullable Point bufferSize; + + /** + * Sets the data from the other data. + * @hide + */ + public void set(PersistedTaskSnapshotData other) { + taskSize = other.taskSize; + contentInsets = other.contentInsets; + bufferSize = other.bufferSize; + } + + /** + * Sets the data from the provided {@param snapshot}. + * @hide + */ + public void set(TaskSnapshot snapshot) { + if (snapshot == null) { + taskSize = null; + contentInsets = null; + bufferSize = null; + return; + } + final HardwareBuffer buffer = snapshot.getHardwareBuffer(); + taskSize = new Point(snapshot.getTaskSize()); + contentInsets = new Rect(snapshot.getContentInsets()); + bufferSize = buffer != null + ? new Point(buffer.getWidth(), buffer.getHeight()) + : null; + } + } + + /** * If this task is currently running, this is the identifier for it. * If it is not running, this will be -1. * @@ -1782,6 +1841,12 @@ public class ActivityManager { */ public ArrayList<RecentTaskInfo> childrenTaskInfos = new ArrayList<>(); + /** + * Information about the last snapshot taken for this task. + * @hide + */ + public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData(); + public RecentTaskInfo() { } @@ -1798,6 +1863,9 @@ public class ActivityManager { id = source.readInt(); persistentId = source.readInt(); childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader()); + lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR); + lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR); + lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR); super.readFromParcel(source); } @@ -1806,6 +1874,9 @@ public class ActivityManager { dest.writeInt(id); dest.writeInt(persistentId); dest.writeList(childrenTaskInfos); + dest.writeTypedObject(lastSnapshotData.taskSize, flags); + dest.writeTypedObject(lastSnapshotData.contentInsets, flags); + dest.writeTypedObject(lastSnapshotData.bufferSize, flags); super.writeToParcel(dest, flags); } @@ -1825,7 +1896,6 @@ public class ActivityManager { public void dump(PrintWriter pw, String indent) { pw.println(); pw.print(" "); pw.print(" id="); pw.print(persistentId); - pw.print(" stackId="); pw.print(stackId); pw.print(" userId="); pw.print(userId); pw.print(" hasTask="); pw.print((id != -1)); pw.print(" lastActiveTime="); pw.println(lastActiveTime); @@ -1872,6 +1942,12 @@ public class ActivityManager { pw.print(Integer.toHexString(td.getBackgroundColorFloating())); pw.println(" }"); } + pw.print(" "); + pw.print(" lastSnapshotData {"); + pw.print(" taskSize=" + lastSnapshotData.taskSize); + pw.print(" contentInsets=" + lastSnapshotData.contentInsets); + pw.print(" bufferSize=" + lastSnapshotData.bufferSize); + pw.println(" }"); } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c242fd466c41..050f34a21477 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -9972,6 +9972,15 @@ public class Notification implements Parcelable * <p>The shortcut activity will be used when the bubble is expanded. This will display * the shortcut activity in a floating window over the existing foreground activity.</p> * + * <p>When the shortcut is displayed in a bubble, there will be an intent + * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED} + * with {@code true}. You may check this in the onCreate of your activity via: + * + * <pre class="prettyprint"> + * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false); + * </pre> + * </p> + * * <p>If the shortcut has not been published when the bubble notification is sent, * no bubble will be produced. If the shortcut is deleted while the bubble is active, * the bubble will be removed.</p> @@ -10000,6 +10009,15 @@ public class Notification implements Parcelable * app content in a floating window over the existing foreground activity. The intent * should point to a resizable activity. </p> * + * <p>When the activity is displayed in a bubble, there will be an intent + * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED} + * with {@code true}. You may check this in the onCreate of your activity via: + * + * <pre class="prettyprint"> + * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false); + * </pre> + * </p> + * * @throws NullPointerException if intent is null. * @throws NullPointerException if icon is null. */ diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 390d9219ef2a..938ce0d56933 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -53,13 +53,6 @@ public class TaskInfo { public int userId; /** - * The id of the ActivityStack that currently contains this task. - * @hide - */ - @UnsupportedAppUsage - public int stackId; - - /** * The identifier for this task. */ public int taskId; @@ -358,7 +351,6 @@ public class TaskInfo { */ void readFromParcel(Parcel source) { userId = source.readInt(); - stackId = source.readInt(); taskId = source.readInt(); displayId = source.readInt(); isRunning = source.readBoolean(); @@ -394,7 +386,6 @@ public class TaskInfo { */ void writeToParcel(Parcel dest, int flags) { dest.writeInt(userId); - dest.writeInt(stackId); dest.writeInt(taskId); dest.writeInt(displayId); dest.writeBoolean(isRunning); @@ -428,7 +419,7 @@ public class TaskInfo { @Override public String toString() { - return "TaskInfo{userId=" + userId + " stackId=" + stackId + " taskId=" + taskId + return "TaskInfo{userId=" + userId + " taskId=" + taskId + " displayId=" + displayId + " isRunning=" + isRunning + " baseIntent=" + baseIntent + " baseActivity=" + baseActivity diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java index 5e1cbadb458e..9eb9a7bdad07 100644 --- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java +++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java @@ -25,6 +25,7 @@ import android.annotation.TestApi; import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; +import android.stats.devicepolicy.DevicePolicyEnums; import java.util.Locale; @@ -35,6 +36,13 @@ import java.util.Locale; */ @TestApi public final class FullyManagedDeviceProvisioningParams implements Parcelable { + private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM = + "LEAVE_ALL_SYSTEM_APPS_ENABLED"; + private static final String CAN_DEVICE_OWNER_GRANT_SENSOR_PERMISSIONS_PARAM = + "CAN_DEVICE_OWNER_GRANT_SENSOR_PERMISSIONS"; + private static final String TIME_ZONE_PROVIDED_PARAM = "TIME_ZONE_PROVIDED"; + private static final String LOCALE_PROVIDED_PARAM = "LOCALE_PROVIDED"; + @NonNull private final ComponentName mDeviceAdminComponentName; @NonNull private final String mOwnerName; private final boolean mLeaveAllSystemAppsEnabled; @@ -121,6 +129,29 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { } /** + * Logs the provisioning params using {@link DevicePolicyEventLogger}. + */ + public void logParams(@NonNull String callerPackage) { + requireNonNull(callerPackage); + + logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM, mLeaveAllSystemAppsEnabled); + logParam(callerPackage, CAN_DEVICE_OWNER_GRANT_SENSOR_PERMISSIONS_PARAM, + mDeviceOwnerCanGrantSensorsPermissions); + logParam(callerPackage, TIME_ZONE_PROVIDED_PARAM, /* value= */ mTimeZone != null); + logParam(callerPackage, LOCALE_PROVIDED_PARAM, /* value= */ mLocale != null); + } + + private void logParam(String callerPackage, String param, boolean value) { + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_PARAM) + .setStrings(callerPackage) + .setAdmin(mDeviceAdminComponentName) + .setStrings(param) + .setBoolean(value) + .write(); + } + + /** * Builder class for {@link FullyManagedDeviceProvisioningParams} objects. */ public static final class Builder { diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java index 5fe63d142021..1a6099a7f570 100644 --- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java +++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java @@ -25,6 +25,7 @@ import android.annotation.TestApi; import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; +import android.stats.devicepolicy.DevicePolicyEnums; /** * Params required to provision a managed profile, see @@ -34,6 +35,13 @@ import android.os.Parcelable; */ @TestApi public final class ManagedProfileProvisioningParams implements Parcelable { + private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM = + "LEAVE_ALL_SYSTEM_APPS_ENABLED"; + private static final String ORGANIZATION_OWNED_PROVISIONING_PARAM = + "ORGANIZATION_OWNED_PROVISIONING"; + private static final String ACCOUNT_TO_MIGRATE_PROVIDED_PARAM = "ACCOUNT_TO_MIGRATE_PROVIDED"; + private static final String KEEP_MIGRATED_ACCOUNT_PARAM = "KEEP_MIGRATED_ACCOUNT"; + @NonNull private final ComponentName mProfileAdminComponentName; @NonNull private final String mOwnerName; @Nullable private final String mProfileName; @@ -93,6 +101,30 @@ public final class ManagedProfileProvisioningParams implements Parcelable { } /** + * Logs the provisioning params using {@link DevicePolicyEventLogger}. + */ + public void logParams(@NonNull String callerPackage) { + requireNonNull(callerPackage); + + logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM, mLeaveAllSystemAppsEnabled); + logParam(callerPackage, ORGANIZATION_OWNED_PROVISIONING_PARAM, + mOrganizationOwnedProvisioning); + logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountMigrated); + logParam(callerPackage, ACCOUNT_TO_MIGRATE_PROVIDED_PARAM, + /* value= */ mAccountToMigrate != null); + } + + private void logParam(String callerPackage, String param, boolean value) { + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_PARAM) + .setStrings(callerPackage) + .setAdmin(mProfileAdminComponentName) + .setStrings(param) + .setBoolean(value) + .write(); + } + + /** * Builder class for {@link ManagedProfileProvisioningParams} objects. */ public static final class Builder { diff --git a/core/java/android/app/compat/OWNERS b/core/java/android/app/compat/OWNERS new file mode 100644 index 000000000000..f8c3520e9fa8 --- /dev/null +++ b/core/java/android/app/compat/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/compat/OWNERS diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index d7dc86a5c59f..46d8900e59a1 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -63,7 +63,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; -import android.system.Int32Ref; +import android.system.Int64Ref; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -4050,7 +4050,7 @@ public abstract class ContentResolver implements ContentInterface { // Convert to Point, since that's what the API is defined as final Bundle opts = new Bundle(); opts.putParcelable(EXTRA_SIZE, new Point(size.getWidth(), size.getHeight())); - final Int32Ref orientation = new Int32Ref(0); + final Int64Ref orientation = new Int64Ref(0); Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts, diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 30b24044a624..4abd8cd7d37b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5718,7 +5718,7 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; /** - * Used in the extra field in the remote intent. It's astring token passed with the + * Used in the extra field in the remote intent. It's a string token passed with the * remote intent. */ public static final String EXTRA_REMOTE_INTENT_TOKEN = @@ -6062,6 +6062,16 @@ public class Intent implements Parcelable, Cloneable { */ public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON"; + /** + * A boolean extra indicating whether an activity is bubbled. Set on the shortcut or + * pending intent provided for the bubble. If the extra is not present or false, then it is not + * bubbled. + * + * @see android.app.Notification.Builder#setBubbleMetadata(Notification.BubbleMetadata) + * @see android.app.Notification.BubbleMetadata.Builder#Builder(String) + */ + public static final String EXTRA_IS_BUBBLED = "android.intent.extra.IS_BUBBLED"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/content/om/CriticalOverlayInfo.java b/core/java/android/content/om/CriticalOverlayInfo.java new file mode 100644 index 000000000000..8fbc698f368a --- /dev/null +++ b/core/java/android/content/om/CriticalOverlayInfo.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.om; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +/** + * A subset of {@link OverlayInfo} fields that when changed cause the overlay's settings to be + * completely reinitialized. + * + * @hide + */ +public interface CriticalOverlayInfo { + + /** + * @return the package name of the overlay. + */ + @NonNull + String getPackageName(); + + /** + * @return the unique name of the overlay within its containing package. + */ + @Nullable + String getOverlayName(); + + /** + * @return the target package name of the overlay. + */ + @NonNull + String getTargetPackageName(); + + /** + * @return the name of the target overlayable declaration. + */ + @Nullable + String getTargetOverlayableName(); + + /** + * @return an identifier representing the current overlay. + */ + @NonNull + OverlayIdentifier getOverlayIdentifier(); + + /** + * Returns whether or not the overlay is a {@link FabricatedOverlay}. + */ + boolean isFabricated(); +} diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java new file mode 100644 index 000000000000..d62b47b34dcb --- /dev/null +++ b/core/java/android/content/om/FabricatedOverlay.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.om; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.FabricatedOverlayInternal; +import android.os.FabricatedOverlayInternalEntry; +import android.text.TextUtils; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; + +/** + * Fabricated Runtime Resource Overlays (FRROs) are overlays generated ar runtime. + * + * Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The + * overlayable policies a fabricated overlay fulfills are the same policies the creator of the + * overlay fulfill. For example, a fabricated overlay created by a platform signed package on the + * system partition would fulfil the {@code system} and {@code signature} policies. + * + * The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay + * manager persist across reboots. When the UID is uninstalled, its fabricated overlays are wiped. + * + * Processes with {@link Android.Manifest.permission.CHANGE_OVERLAY_PACKAGES} can manage normal + * overlays and fabricated overlays. + * @hide + */ +public class FabricatedOverlay { + + /** Retrieves the identifier for this fabricated overlay. */ + public OverlayIdentifier getIdentifier() { + return new OverlayIdentifier( + mOverlay.packageName, TextUtils.nullIfEmpty(mOverlay.overlayName)); + } + + public static class Builder { + private final String mOwningPackage; + private final String mName; + private final String mTargetPackage; + private String mTargetOverlayable = ""; + private final ArrayList<FabricatedOverlayInternalEntry> mEntries = new ArrayList<>(); + + /** + * Constructs a build for a fabricated overlay. + * + * @param owningPackage the name of the package that owns the fabricated overlay (must + * be a package name of this UID). + * @param name a name used to uniquely identify the fabricated overlay owned by + * {@param owningPackageName} + * @param targetPackage the name of the package to overlay + */ + public Builder(@NonNull String owningPackage, @NonNull String name, + @NonNull String targetPackage) { + Preconditions.checkStringNotEmpty(owningPackage, + "'owningPackage' must not be empty nor null"); + Preconditions.checkStringNotEmpty(name, + "'name'' must not be empty nor null"); + Preconditions.checkStringNotEmpty(targetPackage, + "'targetPackage' must not be empty nor null"); + + mOwningPackage = owningPackage; + mName = name; + mTargetPackage = targetPackage; + } + + /** + * Sets the name of the overlayable resources to overlay (can be null). + */ + public Builder setTargetOverlayable(@Nullable String targetOverlayable) { + mTargetOverlayable = TextUtils.emptyIfNull(targetOverlayable); + return this; + } + + /** + * Sets the value of + * + * @param resourceName name of the target resource to overlay (in the form + * [package]:type/entry) + * @param dataType the data type of the new value + * @param value the unsigned 32 bit integer representing the new value + * + * @see android.util.TypedValue#type + */ + public Builder setResourceValue(@NonNull String resourceName, int dataType, int value) { + final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry(); + entry.resourceName = resourceName; + entry.dataType = dataType; + entry.data = value; + mEntries.add(entry); + return this; + } + + /** Builds an immutable fabricated overlay. */ + public FabricatedOverlay build() { + final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal(); + overlay.packageName = mOwningPackage; + overlay.overlayName = mName; + overlay.targetPackageName = mTargetPackage; + overlay.targetOverlayable = mTargetOverlayable; + overlay.entries = new ArrayList<>(); + overlay.entries.addAll(mEntries); + return new FabricatedOverlay(overlay); + } + } + + final FabricatedOverlayInternal mOverlay; + private FabricatedOverlay(FabricatedOverlayInternal overlay) { + mOverlay = overlay; + } +} diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl index 0b950b461285..e319d2c4655c 100644 --- a/core/java/android/content/om/IOverlayManager.aidl +++ b/core/java/android/content/om/IOverlayManager.aidl @@ -16,6 +16,7 @@ package android.content.om; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManagerTransaction; @@ -66,6 +67,17 @@ interface IOverlayManager { OverlayInfo getOverlayInfo(in String packageName, in int userId); /** + * Returns information about the overlay with the given package name for the + * specified user. + * + * @param packageName The name of the overlay package. + * @param userId The user to get the OverlayInfo for. + * @return The OverlayInfo for the overlay package; or null if no such + * overlay package exists. + */ + OverlayInfo getOverlayInfoByIdentifier(in OverlayIdentifier packageName, in int userId); + + /** * Request that an overlay package be enabled or disabled when possible to * do so. * @@ -163,7 +175,7 @@ interface IOverlayManager { * Invalidates and removes the idmap for an overlay, * @param packageName The name of the overlay package whose idmap should be deleted. */ - void invalidateCachesForOverlay(in String packageName, in int userIs); + void invalidateCachesForOverlay(in String packageName, in int userId); /** * Perform a series of requests related to overlay packages. This is an diff --git a/core/java/android/content/om/OverlayIdentifier.aidl b/core/java/android/content/om/OverlayIdentifier.aidl new file mode 100644 index 000000000000..d1c7770f9168 --- /dev/null +++ b/core/java/android/content/om/OverlayIdentifier.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.om; + +parcelable OverlayIdentifier; diff --git a/core/java/android/content/om/OverlayIdentifier.java b/core/java/android/content/om/OverlayIdentifier.java new file mode 100644 index 000000000000..454d0d120f2f --- /dev/null +++ b/core/java/android/content/om/OverlayIdentifier.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.om; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Objects; + +/** + * A key used to uniquely identify a Runtime Resource Overlay (RRO). + * + * An overlay always belongs to a package and may optionally have a name associated with it. + * The name helps uniquely identify a particular overlay within a package. + * @hide + */ +/** @hide */ +@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false, + genEqualsHashCode = true, genToString = false) +public class OverlayIdentifier implements Parcelable { + /** + * The package name containing or owning the overlay. + */ + @Nullable + private final String mPackageName; + + /** + * The unique name within the package of the overlay. + */ + @Nullable + private final String mOverlayName; + + /** + * Creates an identifier from a package and unique name within the package. + * + * @param packageName the package containing or owning the overlay + * @param overlayName the unique name of the overlay within the package + */ + public OverlayIdentifier(@NonNull String packageName, @Nullable String overlayName) { + mPackageName = packageName; + mOverlayName = overlayName; + } + + /** + * Creates an identifier for an overlay without a name. + * + * @param packageName the package containing or owning the overlay + */ + public OverlayIdentifier(@NonNull String packageName) { + mPackageName = packageName; + mOverlayName = null; + } + + @Override + public String toString() { + return mOverlayName == null ? mPackageName : mPackageName + ":" + mOverlayName; + } + + /** @hide */ + public static OverlayIdentifier fromString(@NonNull String text) { + final String[] parts = text.split(":", 2); + if (parts.length == 2) { + return new OverlayIdentifier(parts[0], parts[1]); + } else { + return new OverlayIdentifier(parts[0]); + } + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/om/OverlayIdentifier.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Retrieves the package name containing or owning the overlay. + */ + @DataClass.Generated.Member + public @Nullable String getPackageName() { + return mPackageName; + } + + /** + * Retrieves the unique name within the package of the overlay. + */ + @DataClass.Generated.Member + public @Nullable String getOverlayName() { + return mOverlayName; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(OverlayIdentifier other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + OverlayIdentifier that = (OverlayIdentifier) o; + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mPackageName, that.mPackageName) + && Objects.equals(mOverlayName, that.mOverlayName); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mPackageName); + _hash = 31 * _hash + Objects.hashCode(mOverlayName); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mPackageName != null) flg |= 0x1; + if (mOverlayName != null) flg |= 0x2; + dest.writeByte(flg); + if (mPackageName != null) dest.writeString(mPackageName); + if (mOverlayName != null) dest.writeString(mOverlayName); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected OverlayIdentifier(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String packageName = (flg & 0x1) == 0 ? null : in.readString(); + String overlayName = (flg & 0x2) == 0 ? null : in.readString(); + + this.mPackageName = packageName; + this.mOverlayName = overlayName; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<OverlayIdentifier> CREATOR + = new Parcelable.Creator<OverlayIdentifier>() { + @Override + public OverlayIdentifier[] newArray(int size) { + return new OverlayIdentifier[size]; + } + + @Override + public OverlayIdentifier createFromParcel(@NonNull Parcel in) { + return new OverlayIdentifier(in); + } + }; + + @DataClass.Generated( + time = 1612482438728L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/om/OverlayIdentifier.java", + inputSignatures = "private final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mOverlayName\npublic @java.lang.Override java.lang.String toString()\npublic static android.content.om.OverlayIdentifier fromString(java.lang.String)\nclass OverlayIdentifier extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=false)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java index 517e4bda68d7..c66f49cab088 100644 --- a/core/java/android/content/om/OverlayInfo.java +++ b/core/java/android/content/om/OverlayInfo.java @@ -26,6 +26,8 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -37,7 +39,7 @@ import java.util.Objects; * @hide */ @SystemApi -public final class OverlayInfo implements Parcelable { +public final class OverlayInfo implements CriticalOverlayInfo, Parcelable { /** @hide */ @IntDef(prefix = "STATE_", value = { @@ -143,6 +145,14 @@ public final class OverlayInfo implements Parcelable { public final String packageName; /** + * The unique name within the package of the overlay. + * + * @hide + */ + @Nullable + public final String overlayName; + + /** * Package name of the target package * * @hide @@ -201,6 +211,14 @@ public final class OverlayInfo implements Parcelable { */ public final boolean isMutable; + private OverlayIdentifier mIdentifierCached; + + /** + * + * @hide + */ + public final boolean isFabricated; + /** * Create a new OverlayInfo based on source with an updated state. * @@ -210,17 +228,28 @@ public final class OverlayInfo implements Parcelable { * @hide */ public OverlayInfo(@NonNull OverlayInfo source, @State int state) { - this(source.packageName, source.targetPackageName, source.targetOverlayableName, - source.category, source.baseCodePath, state, source.userId, source.priority, - source.isMutable); + this(source.packageName, source.overlayName, source.targetPackageName, + source.targetOverlayableName, source.category, source.baseCodePath, state, + source.userId, source.priority, source.isMutable, source.isFabricated); } /** @hide */ + @VisibleForTesting public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName, @Nullable String targetOverlayableName, @Nullable String category, - @NonNull String baseCodePath, int state, int userId, - int priority, boolean isMutable) { + @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) { + this(packageName, null /* overlayName */, targetPackageName, targetOverlayableName, + category, baseCodePath, state, userId, priority, isMutable, + false /* isFabricated */); + } + + /** @hide */ + public OverlayInfo(@NonNull String packageName, @Nullable String overlayName, + @NonNull String targetPackageName, @Nullable String targetOverlayableName, + @Nullable String category, @NonNull String baseCodePath, int state, int userId, + int priority, boolean isMutable, boolean isFabricated) { this.packageName = packageName; + this.overlayName = overlayName; this.targetPackageName = targetPackageName; this.targetOverlayableName = targetOverlayableName; this.category = category; @@ -229,12 +258,14 @@ public final class OverlayInfo implements Parcelable { this.userId = userId; this.priority = priority; this.isMutable = isMutable; + this.isFabricated = isFabricated; ensureValidState(); } /** @hide */ public OverlayInfo(Parcel source) { packageName = source.readString(); + overlayName = source.readString(); targetPackageName = source.readString(); targetOverlayableName = source.readString(); category = source.readString(); @@ -243,13 +274,15 @@ public final class OverlayInfo implements Parcelable { userId = source.readInt(); priority = source.readInt(); isMutable = source.readBoolean(); + isFabricated = source.readBoolean(); ensureValidState(); } /** - * Returns package name of the current overlay. + * {@inheritDoc} * @hide */ + @Override @SystemApi @NonNull public String getPackageName() { @@ -257,9 +290,20 @@ public final class OverlayInfo implements Parcelable { } /** - * Returns the target package name of the current overlay. + * {@inheritDoc} * @hide */ + @Override + @Nullable + public String getOverlayName() { + return overlayName; + } + + /** + * {@inheritDoc} + * @hide + */ + @Override @SystemApi @NonNull public String getTargetPackageName() { @@ -268,7 +312,8 @@ public final class OverlayInfo implements Parcelable { /** * Returns the category of the current overlay. - * @hide\ + * + * @hide */ @SystemApi @Nullable @@ -278,6 +323,7 @@ public final class OverlayInfo implements Parcelable { /** * Returns user handle for which this overlay applies to. + * * @hide */ @SystemApi @@ -287,15 +333,47 @@ public final class OverlayInfo implements Parcelable { } /** - * Returns name of the target overlayable declaration. + * {@inheritDoc} * @hide */ + @Override @SystemApi @Nullable public String getTargetOverlayableName() { return targetOverlayableName; } + /** + * {@inheritDoc} + * @hide + */ + @Override + public boolean isFabricated() { + return isFabricated; + } + + /** + * Full path to the base APK or fabricated overlay for this overlay package. + * + * @hide + */ + public String getBaseCodePath() { + return baseCodePath; + } + + /** + * {@inheritDoc} + * @hide + */ + @Override + @NonNull + public OverlayIdentifier getOverlayIdentifier() { + if (mIdentifierCached == null) { + mIdentifierCached = new OverlayIdentifier(packageName, overlayName); + } + return mIdentifierCached; + } + @SuppressWarnings("ConstantConditions") private void ensureValidState() { if (packageName == null) { @@ -330,6 +408,7 @@ public final class OverlayInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(packageName); + dest.writeString(overlayName); dest.writeString(targetPackageName); dest.writeString(targetOverlayableName); dest.writeString(category); @@ -338,6 +417,7 @@ public final class OverlayInfo implements Parcelable { dest.writeInt(userId); dest.writeInt(priority); dest.writeBoolean(isMutable); + dest.writeBoolean(isFabricated); } public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR = @@ -410,6 +490,7 @@ public final class OverlayInfo implements Parcelable { result = prime * result + userId; result = prime * result + state; result = prime * result + ((packageName == null) ? 0 : packageName.hashCode()); + result = prime * result + ((overlayName == null) ? 0 : overlayName.hashCode()); result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode()); result = prime * result + ((targetOverlayableName == null) ? 0 : targetOverlayableName.hashCode()); @@ -439,6 +520,9 @@ public final class OverlayInfo implements Parcelable { if (!packageName.equals(other.packageName)) { return false; } + if (!Objects.equals(overlayName, other.overlayName)) { + return false; + } if (!targetPackageName.equals(other.targetPackageName)) { return false; } @@ -457,9 +541,13 @@ public final class OverlayInfo implements Parcelable { @NonNull @Override public String toString() { - return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName - + ((targetOverlayableName == null) ? "" - : ", targetOverlayable=" + targetOverlayableName) - + ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }"; + return "OverlayInfo {" + + "packageName=" + packageName + + ", overlayName=" + overlayName + + ", targetPackage=" + targetPackageName + + ", targetOverlayable=" + targetOverlayableName + + ", state=" + state + " (" + stateToString(state) + ")," + + ", userId=" + userId + + " }"; } } diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java index 7c14c2891d01..0f7e01b234c2 100644 --- a/core/java/android/content/om/OverlayManager.java +++ b/core/java/android/content/om/OverlayManager.java @@ -205,6 +205,25 @@ public class OverlayManager { } /** + * Returns information about the overlay represented by the identifier for the specified user. + * + * @param overlay the identifier representing the overlay + * @param userHandle the user of which to get overlay state info + * @return the overlay info or null if the overlay cannot be found + * + * @hide + */ + @Nullable + public OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay, + @NonNull final UserHandle userHandle) { + try { + return mService.getOverlayInfoByIdentifier(overlay, userHandle.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns information about all overlays for the given target package for * the specified user. The returned list is ordered according to the * overlay priority with the highest priority at the end of the list. diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java index 1fa8973c35b5..73be0ffbf467 100644 --- a/core/java/android/content/om/OverlayManagerTransaction.java +++ b/core/java/android/content/om/OverlayManagerTransaction.java @@ -20,6 +20,9 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; @@ -29,6 +32,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Locale; /** * Container for a batch of requests to the OverlayManagerService. @@ -60,12 +64,13 @@ public class OverlayManagerTransaction private OverlayManagerTransaction(@NonNull final Parcel source) { final int size = source.readInt(); - mRequests = new ArrayList<Request>(size); + mRequests = new ArrayList<>(size); for (int i = 0; i < size; i++) { final int request = source.readInt(); - final String packageName = source.readString(); + final OverlayIdentifier overlay = source.readParcelable(null); final int userId = source.readInt(); - mRequests.add(new Request(request, packageName, userId)); + final Bundle extras = source.readBundle(null); + mRequests.add(new Request(request, overlay, userId, extras)); } } @@ -95,22 +100,36 @@ public class OverlayManagerTransaction public static final int TYPE_SET_ENABLED = 0; public static final int TYPE_SET_DISABLED = 1; + public static final int TYPE_REGISTER_FABRICATED = 2; + public static final int TYPE_UNREGISTER_FABRICATED = 3; - @RequestType public final int type; - public final String packageName; + public static final String BUNDLE_FABRICATED_OVERLAY = "fabricated_overlay"; + + @RequestType + public final int type; + @NonNull + public final OverlayIdentifier overlay; public final int userId; + @Nullable + public final Bundle extras; - public Request(@RequestType final int type, @NonNull final String packageName, + public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, final int userId) { + this(type, overlay, userId, null /* extras */); + } + + public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, + final int userId, @Nullable Bundle extras) { this.type = type; - this.packageName = packageName; + this.overlay = overlay; this.userId = userId; + this.extras = extras; } @Override public String toString() { - return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}", - type, typeToString(), packageName, userId); + return String.format(Locale.US, "Request{type=0x%02x (%s), overlay=%s, userId=%d}", + type, typeToString(), overlay, userId); } /** @@ -123,6 +142,8 @@ public class OverlayManagerTransaction switch (type) { case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED"; case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED"; + case TYPE_REGISTER_FABRICATED: return "TYPE_REGISTER_FABRICATED"; + case TYPE_UNREGISTER_FABRICATED: return "TYPE_UNREGISTER_FABRICATED"; default: return String.format("TYPE_UNKNOWN (0x%02x)", type); } } @@ -152,22 +173,56 @@ public class OverlayManagerTransaction * longer affect the resources of the target package. If the target is * currently running, its outdated resources will be replaced by new ones. * - * @param packageName The name of the overlay package. + * @param overlay The name of the overlay package. * @param enable true to enable the overlay, false to disable it. * @return this Builder object, so you can chain additional requests */ - public Builder setEnabled(@NonNull String packageName, boolean enable) { - return setEnabled(packageName, enable, UserHandle.myUserId()); + public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable) { + return setEnabled(overlay, enable, UserHandle.myUserId()); } /** * @hide */ - public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) { - checkNotNull(packageName); + public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) { + checkNotNull(overlay); @Request.RequestType final int type = enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED; - mRequests.add(new Request(type, packageName, userId)); + mRequests.add(new Request(type, overlay, userId)); + return this; + } + + /** + * Registers the fabricated overlay with the overlay manager so it can be enabled and + * disabled for any user. + * + * The fabricated overlay is initialized in a disabled state. If an overlay is re-registered + * the existing overlay will be replaced by the newly registered overlay and the enabled + * state of the overlay will be left unchanged if the target package and target overlayable + * have not changed. + * + * @param overlay the overlay to register with the overlay manager + * + * @hide + */ + public Builder registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) { + final Bundle extras = new Bundle(); + extras.putParcelable(Request.BUNDLE_FABRICATED_OVERLAY, overlay.mOverlay); + mRequests.add(new Request(Request.TYPE_REGISTER_FABRICATED, overlay.getIdentifier(), + UserHandle.USER_ALL, extras)); + return this; + } + + /** + * Disables and removes the overlay from the overlay manager for all users. + * + * @param overlay the overlay to disable and remove + * + * @hide + */ + public Builder unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) { + mRequests.add(new Request(Request.TYPE_UNREGISTER_FABRICATED, overlay, + UserHandle.USER_ALL)); return this; } @@ -195,8 +250,9 @@ public class OverlayManagerTransaction for (int i = 0; i < size; i++) { final Request req = mRequests.get(i); dest.writeInt(req.type); - dest.writeString(req.packageName); + dest.writeParcelable(req.overlay, flags); dest.writeInt(req.userId); + dest.writeBundle(req.extras); } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0819d1743ad6..bf8d1f6ab07b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6151,6 +6151,56 @@ public class PackageParser { } /** + * Returns whether this instance is currently signed, or has ever been signed, with a + * signing certificate from the provided {@link Set} of {@code certDigests}. + * + * <p>The provided {@code certDigests} should contain the SHA-256 digest of the DER encoding + * of each trusted certificate with the digest characters in upper case. If this instance + * has multiple signers then all signers must be in the provided {@code Set}. If this + * instance has a signing lineage then this method will return true if any of the previous + * signers in the lineage match one of the entries in the {@code Set}. + */ + public boolean hasAncestorOrSelfWithDigest(Set<String> certDigests) { + if (this == UNKNOWN || certDigests == null || certDigests.size() == 0) { + return false; + } + // If an app is signed by multiple signers then all of the signers must be in the Set. + if (signatures.length > 1) { + // If the Set has less elements than the number of signatures then immediately + // return false as there's no way to satisfy the requirement of all signatures being + // in the Set. + if (certDigests.size() < signatures.length) { + return false; + } + for (Signature signature : signatures) { + String signatureDigest = PackageUtils.computeSha256Digest( + signature.toByteArray()); + if (!certDigests.contains(signatureDigest)) { + return false; + } + } + return true; + } + + String signatureDigest = PackageUtils.computeSha256Digest(signatures[0].toByteArray()); + if (certDigests.contains(signatureDigest)) { + return true; + } + if (hasPastSigningCertificates()) { + // The last element in the pastSigningCertificates array is the current signer; + // since that was verified above just check all the signers in the lineage. + for (int i = 0; i < pastSigningCertificates.length - 1; i++) { + signatureDigest = PackageUtils.computeSha256Digest( + pastSigningCertificates[i].toByteArray()); + if (certDigests.contains(signatureDigest)) { + return true; + } + } + } + return false; + } + + /** * Returns the SigningDetails with a descendant (or same) signer after verifying the * descendant has the same, a superset, or a subset of the lineage of the ancestor. * diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 35f02a8d5dd2..a2e533af64a0 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -30,6 +30,7 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Set; /** * Information you can retrieve about a particular security permission @@ -278,6 +279,15 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { @SystemApi public static final int PROTECTION_FLAG_ROLE = 0x4000000; + /** + * Additional flag for {@link #protectionLevel}, correspoinding to the {@code knownSigner} value + * of {@link android.R.attr#protectionLevel}. + * + * @hide + */ + @SystemApi + public static final int PROTECTION_FLAG_KNOWN_SIGNER = 0x8000000; + /** @hide */ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = { PROTECTION_FLAG_PRIVILEGED, @@ -303,6 +313,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { PROTECTION_FLAG_RETAIL_DEMO, PROTECTION_FLAG_RECENTS, PROTECTION_FLAG_ROLE, + PROTECTION_FLAG_KNOWN_SIGNER, }) @Retention(RetentionPolicy.SOURCE) public @interface ProtectionFlags {} @@ -466,6 +477,15 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { */ public @Nullable CharSequence nonLocalizedDescription; + /** + * A {@link Set} of trusted signing certificate digests. If this permission has the {@link + * #PROTECTION_FLAG_KNOWN_SIGNER} flag set the permission will be granted to a requesting app + * if the app is signed by any of these certificates. + * + * @hide + */ + public @Nullable Set<String> knownCerts; + /** @hide */ public static int fixProtectionLevel(int level) { if (level == PROTECTION_SIGNATURE_OR_SYSTEM) { @@ -570,6 +590,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level & PermissionInfo.PROTECTION_FLAG_ROLE) != 0) { protLevel.append("|role"); } + if ((level & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0) { + protLevel.append("|knownSigner"); + } return protLevel.toString(); } diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index fb0d90490567..9a84ded99c67 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -655,6 +655,7 @@ public class PackageInfoWithoutStateUtils { pi.protectionLevel = p.getProtectionLevel(); pi.descriptionRes = p.getDescriptionRes(); pi.flags = p.getFlags(); + pi.knownCerts = p.getKnownCerts(); if ((flags & PackageManager.GET_META_DATA) == 0) { return pi; diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 66bdb9bb864a..b7aa30f00691 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -2807,7 +2807,7 @@ public class ParsingPackageUtils { * limits length of the name to the {@link #MAX_FILE_NAME_SIZE}. * @return Success if it's valid. */ - public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator, + public static String validateName(String name, boolean requireSeparator, boolean requireFilename) { final int N = name.length(); boolean hasSep = false; @@ -2828,18 +2828,28 @@ public class ParsingPackageUtils { front = true; continue; } - return input.error("bad character '" + c + "'"); + return "bad character '" + c + "'"; } if (requireFilename) { if (!FileUtils.isValidExtFilename(name)) { - return input.error("Invalid filename"); + return "Invalid filename"; } else if (N > MAX_FILE_NAME_SIZE) { - return input.error("the length of the name is greater than " + MAX_FILE_NAME_SIZE); + return "the length of the name is greater than " + MAX_FILE_NAME_SIZE; } } - return hasSep || !requireSeparator - ? input.success(null) - : input.error("must have at least one '.' separator"); + return hasSep || !requireSeparator ? null : "must have at least one '.' separator"; + } + + /** + * @see #validateName(String, boolean, boolean) + */ + public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator, + boolean requireFilename) { + final String errorMessage = validateName(name, requireSeparator, requireFilename); + if (errorMessage != null) { + return input.error(errorMessage); + } + return input.success(null); } /** diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java index f99a0b1dcadb..35bb33c84d56 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermission.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java @@ -26,6 +26,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; +import java.util.Set; + /** @hide */ public class ParsedPermission extends ParsedComponent { @@ -39,6 +41,8 @@ public class ParsedPermission extends ParsedComponent { boolean tree; @Nullable private ParsedPermissionGroup parsedPermissionGroup; + @Nullable + Set<String> knownCerts; @VisibleForTesting public ParsedPermission() { @@ -81,6 +85,10 @@ public class ParsedPermission extends ParsedComponent { return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE; } + public @Nullable Set<String> getKnownCerts() { + return knownCerts; + } + public int calculateFootprint() { int size = getName().length(); if (getNonLocalizedLabel() != null) { diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java index 9012b5ce2b1e..a7cecbee8aec 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java @@ -25,6 +25,7 @@ import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.R; @@ -32,6 +33,8 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.Locale; +import java.util.Set; /** @hide */ public class ParsedPermissionUtils { @@ -90,6 +93,43 @@ public class ParsedPermissionUtils { permission.flags = sa.getInt( R.styleable.AndroidManifestPermission_permissionFlags, 0); + final int knownCertsResource = sa.getResourceId( + R.styleable.AndroidManifestPermission_knownCerts, 0); + if (knownCertsResource != 0) { + // The knownCerts attribute supports both a string array resource as well as a + // string resource for the case where the permission should only be granted to a + // single known signer. + final String resourceType = res.getResourceTypeName(knownCertsResource); + if (resourceType.equals("array")) { + final String[] knownCerts = res.getStringArray(knownCertsResource); + if (knownCerts != null) { + // Convert the provided digest to upper case for consistent Set membership + // checks when verifying the signing certificate digests of requesting apps. + permission.knownCerts = new ArraySet<>(); + for (String knownCert : knownCerts) { + permission.knownCerts.add(knownCert.toUpperCase(Locale.US)); + } + } + } else { + final String knownCert = res.getString(knownCertsResource); + if (knownCert != null) { + permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US)); + } + } + if (permission.knownCerts == null) { + Slog.w(TAG, packageName + " defines a knownSigner permission but" + + " the provided knownCerts resource is null"); + } + } else { + // If the knownCerts resource ID is null check if the app specified a string + // value for the attribute representing a single trusted signer. + final String knownCert = sa.getString( + R.styleable.AndroidManifestPermission_knownCerts); + if (knownCert != null) { + permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US)); + } + } + // For now only platform runtime permissions can be restricted if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) { permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED; diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java index db047f8ba696..f551d6a175da 100644 --- a/core/java/android/graphics/fonts/FontUpdateRequest.java +++ b/core/java/android/graphics/fonts/FontUpdateRequest.java @@ -16,18 +16,33 @@ package android.graphics.fonts; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.text.FontConfig; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Represents a font update request. Currently only font install request is supported. * @hide */ -// TODO: Support font config update. public final class FontUpdateRequest implements Parcelable { + public static final int TYPE_UPDATE_FONT_FILE = 0; + public static final int TYPE_UPDATE_FONT_FAMILY = 1; + + @IntDef(prefix = "TYPE_", value = { + TYPE_UPDATE_FONT_FILE, + TYPE_UPDATE_FONT_FAMILY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + public static final Creator<FontUpdateRequest> CREATOR = new Creator<FontUpdateRequest>() { @Override public FontUpdateRequest createFromParcel(Parcel in) { @@ -40,39 +55,67 @@ public final class FontUpdateRequest implements Parcelable { } }; - @NonNull + private final @Type int mType; + // NonNull if mType == TYPE_UPDATE_FONT_FILE. + @Nullable private final ParcelFileDescriptor mFd; - @NonNull + // NonNull if mType == TYPE_UPDATE_FONT_FILE. + @Nullable private final byte[] mSignature; + // NonNull if mType == TYPE_UPDATE_FONT_FAMILY. + @Nullable + private final FontConfig.FontFamily mFontFamily; public FontUpdateRequest(@NonNull ParcelFileDescriptor fd, @NonNull byte[] signature) { + mType = TYPE_UPDATE_FONT_FILE; mFd = fd; mSignature = signature; + mFontFamily = null; + } + + public FontUpdateRequest(@NonNull FontConfig.FontFamily fontFamily) { + mType = TYPE_UPDATE_FONT_FAMILY; + mFd = null; + mSignature = null; + mFontFamily = fontFamily; } - private FontUpdateRequest(Parcel in) { + protected FontUpdateRequest(Parcel in) { + mType = in.readInt(); mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); mSignature = in.readBlob(); + mFontFamily = in.readParcelable(FontConfig.FontFamily.class.getClassLoader()); } - @NonNull + public @Type int getType() { + return mType; + } + + @Nullable public ParcelFileDescriptor getFd() { return mFd; } - @NonNull + @Nullable public byte[] getSignature() { return mSignature; } + @Nullable + public FontConfig.FontFamily getFontFamily() { + return mFontFamily; + } + @Override public int describeContents() { - return Parcelable.CONTENTS_FILE_DESCRIPTOR; + return mFd != null ? mFd.describeContents() : 0; } @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); dest.writeParcelable(mFd, flags); dest.writeBlob(mSignature); + dest.writeParcelable(mFontFamily, flags); } } diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 08b1e245dc83..5f5697a4ed0b 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -236,7 +236,7 @@ public class BiometricManager { @RequiresPermission(TEST_BIOMETRIC) public BiometricTestSession createTestSession(int sensorId) { try { - return new BiometricTestSession(mContext, + return new BiometricTestSession(mContext, sensorId, mService.createTestSession(sensorId, mContext.getOpPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java index 2b689899af01..1c3560882f1b 100644 --- a/core/java/android/hardware/biometrics/BiometricTestSession.java +++ b/core/java/android/hardware/biometrics/BiometricTestSession.java @@ -25,6 +25,7 @@ import android.content.Context; import android.hardware.fingerprint.FingerprintManager; import android.os.RemoteException; import android.util.ArraySet; +import android.util.Log; /** * Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and @@ -33,7 +34,10 @@ import android.util.ArraySet; */ @TestApi public class BiometricTestSession implements AutoCloseable { + private static final String TAG = "BiometricTestSession"; + private final Context mContext; + private final int mSensorId; private final ITestSession mTestSession; // Keep track of users that were tested, which need to be cleaned up when finishing. @@ -42,8 +46,10 @@ public class BiometricTestSession implements AutoCloseable { /** * @hide */ - public BiometricTestSession(@NonNull Context context, @NonNull ITestSession testSession) { + public BiometricTestSession(@NonNull Context context, int sensorId, + @NonNull ITestSession testSession) { mContext = context; + mSensorId = sensorId; mTestSession = testSession; mTestedUsers = new ArraySet<>(); setTestHalEnabled(true); @@ -61,6 +67,7 @@ public class BiometricTestSession implements AutoCloseable { @RequiresPermission(TEST_BIOMETRIC) private void setTestHalEnabled(boolean enabled) { try { + Log.w(TAG, "setTestHalEnabled, sensor: " + mSensorId + " enabled: " + enabled); mTestSession.setTestHalEnabled(enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 188a2a47fca0..a614ebfe1793 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -153,7 +153,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing @RequiresPermission(TEST_BIOMETRIC) public BiometricTestSession createTestSession(int sensorId) { try { - return new BiometricTestSession(mContext, + return new BiometricTestSession(mContext, sensorId, mService.createTestSession(sensorId, mContext.getOpPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java index e01e5aecc7e6..f7c1c4bb6d96 100644 --- a/core/java/android/net/LocalSocketImpl.java +++ b/core/java/android/net/LocalSocketImpl.java @@ -19,7 +19,6 @@ package android.net; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.system.ErrnoException; -import android.system.Int32Ref; import android.system.Os; import android.system.OsConstants; import android.system.StructLinger; @@ -65,14 +64,11 @@ class LocalSocketImpl public int available() throws IOException { FileDescriptor myFd = fd; if (myFd == null) throw new IOException("socket closed"); - - Int32Ref avail = new Int32Ref(0); try { - Os.ioctlInt(myFd, OsConstants.FIONREAD, avail); + return Os.ioctlInt(myFd, OsConstants.FIONREAD); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } - return avail.value; } /** {@inheritDoc} */ @@ -134,7 +130,7 @@ class LocalSocketImpl public void write (byte[] b) throws IOException { write(b, 0, b.length); } - + /** {@inheritDoc} */ @Override public void write (byte[] b, int off, int len) throws IOException { @@ -255,7 +251,7 @@ class LocalSocketImpl /** note timeout presently ignored */ protected void connect(LocalSocketAddress address, int timeout) throws IOException - { + { if (fd == null) { throw new IOException("socket not created"); } @@ -339,7 +335,7 @@ class LocalSocketImpl * @throws IOException if socket has been closed or cannot be created. */ protected OutputStream getOutputStream() throws IOException - { + { if (fd == null) { throw new IOException("socket not created"); } diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index f2b466d7fa5d..72a6e16c7df5 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -47,6 +47,7 @@ public abstract class BatteryConsumer { POWER_COMPONENT_SYSTEM_SERVICES, POWER_COMPONENT_SENSORS, POWER_COMPONENT_GNSS, + POWER_COMPONENT_SCREEN, }) @Retention(RetentionPolicy.SOURCE) public static @interface PowerComponent { @@ -63,8 +64,9 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_MOBILE_RADIO = 8; public static final int POWER_COMPONENT_SENSORS = 9; public static final int POWER_COMPONENT_GNSS = 10; + public static final int POWER_COMPONENT_SCREEN = 13; - public static final int POWER_COMPONENT_COUNT = 11; + public static final int POWER_COMPONENT_COUNT = 14; public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; @@ -85,6 +87,7 @@ public abstract class BatteryConsumer { TIME_COMPONENT_MOBILE_RADIO, TIME_COMPONENT_SENSORS, TIME_COMPONENT_GNSS, + TIME_COMPONENT_SCREEN, }) @Retention(RetentionPolicy.SOURCE) public static @interface TimeComponent { @@ -101,8 +104,9 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_MOBILE_RADIO = 8; public static final int TIME_COMPONENT_SENSORS = 9; public static final int TIME_COMPONENT_GNSS = 10; + public static final int TIME_COMPONENT_SCREEN = 13; - public static final int TIME_COMPONENT_COUNT = 11; + public static final int TIME_COMPONENT_COUNT = 14; public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999; diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 62951246a43b..124c0b00b2b2 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -1359,8 +1359,17 @@ public class Environment { } final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); - return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, - uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; + final String opPackageName = context.getOpPackageName(); + + if (appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid, + opPackageName) == AppOpsManager.MODE_ALLOWED) { + return true; + } + + // Legacy external storage access is granted to instrumentations invoked with + // "--no-isolated-storage" flag. + return appOps.noteOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid, + opPackageName) == AppOpsManager.MODE_ALLOWED; } private static boolean isScopedStorageEnforced(boolean defaultScopedStorage, diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index d1ca6a560c3f..a828077ac055 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -99,12 +99,13 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela private boolean mExcludeFromBatteryUsageStats; public Builder(int customPowerComponentCount, int customTimeComponentCount, - BatteryStats.Uid batteryStatsUid) { + @NonNull BatteryStats.Uid batteryStatsUid) { super(customPowerComponentCount, customTimeComponentCount); mBatteryStatsUid = batteryStatsUid; mUid = batteryStatsUid.getUid(); } + @NonNull public BatteryStats.Uid getBatteryStatsUid() { return mBatteryStatsUid; } diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java index cbc0479a4c07..20f05981e962 100644 --- a/core/java/android/view/SurfaceSession.java +++ b/core/java/android/view/SurfaceSession.java @@ -32,7 +32,6 @@ public final class SurfaceSession { private static native long nativeCreate(); private static native void nativeDestroy(long ptr); - private static native void nativeKill(long ptr); /** Create a new connection with the surface flinger. */ @UnsupportedAppUsage @@ -44,22 +43,22 @@ public final class SurfaceSession { @Override protected void finalize() throws Throwable { try { - if (mNativeClient != 0) { - nativeDestroy(mNativeClient); - } + kill(); } finally { super.finalize(); } } /** - * Forcibly detach native resources associated with this object. - * Unlike destroy(), after this call any surfaces that were created - * from the session will no longer work. + * Remove the reference to the native Session object. The native object may still exist if + * there are other references to it, but it cannot be accessed from this Java object anymore. */ @UnsupportedAppUsage public void kill() { - nativeKill(mNativeClient); + if (mNativeClient != 0) { + nativeDestroy(mNativeClient); + mNativeClient = 0; + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4716141ee8d3..755ae31c077b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1913,25 +1913,18 @@ public final class ViewRootImpl implements ViewParent, private boolean updateBoundsLayer(SurfaceControl.Transaction t) { if (mBoundsLayer != null) { setBoundsLayerCrop(t); - t.deferTransactionUntil(mBoundsLayer, getSurfaceControl(), - mSurface.getNextFrameNumber()); return true; } return false; } - private void prepareSurfaces(boolean sizeChanged) { + private void prepareSurfaces() { final SurfaceControl.Transaction t = mTransaction; final SurfaceControl sc = getSurfaceControl(); if (!sc.isValid()) return; - boolean applyTransaction = updateBoundsLayer(t); - if (sizeChanged) { - applyTransaction = true; - t.setBufferSize(sc, mSurfaceSize.x, mSurfaceSize.y); - } - if (applyTransaction) { - t.apply(); + if (updateBoundsLayer(t)) { + mergeWithNextTransaction(t, mSurface.getNextFrameNumber()); } } @@ -3036,7 +3029,7 @@ public final class ViewRootImpl implements ViewParent, // stopping, but on the client side it doesn't get stopped since it's restarted quick // enough. WMS doesn't want to keep around old children since they will leak when the // client creates new children. - prepareSurfaces(surfaceSizeChanged); + prepareSurfaces(); } final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 90c8e17a6984..7b2bb73ff562 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -115,7 +115,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** @@ -1083,19 +1082,6 @@ public final class InputMethodManager { } } - private static class ImeThreadFactory implements ThreadFactory { - private final String mThreadName; - - ImeThreadFactory(String name) { - mThreadName = name; - } - - @Override - public Thread newThread(Runnable r) { - return new Thread(r, mThreadName); - } - } - final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index c10ffbee686a..61ff36c09cb9 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.ColorInt; +import android.annotation.IntDef; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -30,6 +31,9 @@ import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * This class performs the graphical effect used at the edges of scrollable widgets * when the user scrolls beyond the content bounds in 2D space. @@ -55,6 +59,24 @@ public class EdgeEffect { */ public static final BlendMode DEFAULT_BLEND_MODE = BlendMode.SRC_ATOP; + /** + * Use a color edge glow for the edge effect. From XML, use + * <code>android:edgeEffectType="glow"</code>. + */ + public static final int TYPE_GLOW = 0; + + /** + * Use a stretch for the edge effect. From XML, use + * <code>android:edgeEffectType="stretch"</code>. + */ + public static final int TYPE_STRETCH = 1; + + /** @hide */ + @IntDef({TYPE_GLOW, TYPE_STRETCH}) + @Retention(RetentionPolicy.SOURCE) + public @interface EdgeEffectType { + } + @SuppressWarnings("UnusedDeclaration") private static final String TAG = "EdgeEffect"; @@ -121,6 +143,7 @@ public class EdgeEffect { private float mBaseGlowScale; private float mDisplacement = 0.5f; private float mTargetDisplacement = 0.5f; + private @EdgeEffectType int mEdgeEffectType = TYPE_GLOW; /** * Construct a new EdgeEffect with a theme appropriate for the provided context. @@ -132,6 +155,8 @@ public class EdgeEffect { com.android.internal.R.styleable.EdgeEffect); final int themeColor = a.getColor( com.android.internal.R.styleable.EdgeEffect_colorEdgeEffect, 0xff666666); + mEdgeEffectType = a.getInt( + com.android.internal.R.styleable.EdgeEffect_edgeEffectType, TYPE_GLOW); a.recycle(); mPaint.setColor((themeColor & 0xffffff) | 0x33000000); mPaint.setStyle(Paint.Style.FILL); @@ -309,6 +334,17 @@ public class EdgeEffect { } /** + * Sets the edge effect type to use. The default without a theme attribute set is + * {@link EdgeEffect#TYPE_GLOW}. + * + * @param type The edge effect type to use. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setType(@EdgeEffectType int type) { + mEdgeEffectType = type; + } + + /** * Set or clear the blend mode. A blend mode defines how source pixels * (generated by a drawing command) are composited with the destination pixels * (content of the render target). @@ -333,6 +369,15 @@ public class EdgeEffect { return mPaint.getColor(); } + /** + * Return the edge effect type to use. + * + * @return The edge effect type to use. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public @EdgeEffectType int getType() { + return mEdgeEffectType; + } /** * Returns the blend mode. A blend mode defines how source pixels diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java index b484dfacbf6c..2904a8c889a2 100644 --- a/core/java/android/widget/ToastPresenter.java +++ b/core/java/android/widget/ToastPresenter.java @@ -172,6 +172,22 @@ public class ToastPresenter { } /** + * Update the LayoutParameters of the currently showing toast view. This is used for layout + * updates based on orientation changes. + */ + public void updateLayoutParams(int xOffset, int yOffset, float horizontalMargin, + float verticalMargin, int gravity) { + checkState(mView != null, "Toast must be showing to update its layout parameters."); + Configuration config = mResources.getConfiguration(); + mParams.gravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection()); + mParams.x = xOffset; + mParams.y = yOffset; + mParams.horizontalMargin = horizontalMargin; + mParams.verticalMargin = verticalMargin; + addToastView(); + } + + /** * Sets {@link WindowManager.LayoutParams#SYSTEM_FLAG_SHOW_FOR_ALL_USERS} flag if {@code * packageName} is a cross-user package. * @@ -221,18 +237,7 @@ public class ToastPresenter { adjustLayoutParams(mParams, windowToken, duration, gravity, xOffset, yOffset, horizontalMargin, verticalMargin, removeWindowAnimations); - if (mView.getParent() != null) { - mWindowManager.removeView(mView); - } - try { - mWindowManager.addView(mView, mParams); - } catch (WindowManager.BadTokenException e) { - // Since the notification manager service cancels the token right after it notifies us - // to cancel the toast there is an inherent race and we may attempt to add a window - // after the token has been invalidated. Let us hedge against that. - Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e); - return; - } + addToastView(); trySendAccessibilityEvent(mView, mPackageName); if (callback != null) { try { @@ -288,4 +293,19 @@ public class ToastPresenter { view.dispatchPopulateAccessibilityEvent(event); mAccessibilityManager.sendAccessibilityEvent(event); } + + private void addToastView() { + if (mView.getParent() != null) { + mWindowManager.removeView(mView); + } + try { + mWindowManager.addView(mView, mParams); + } catch (WindowManager.BadTokenException e) { + // Since the notification manager service cancels the token right after it notifies us + // to cancel the toast there is an inherent race and we may attempt to add a window + // after the token has been invalidated. Let us hedge against that. + Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e); + return; + } + } } diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index 16b6f3a155f8..13130902feb8 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -83,7 +83,7 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { private double getMeasuredOrEstimatedPower(long measuredEnergyUJ, long durationMs) { if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - return mAhToUJ(measuredEnergyUJ); + return uJtoMah(measuredEnergyUJ); } else { return mPowerEstimator.calculatePower(durationMs); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1f7a7aa42669..6e41b3f4ae06 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -104,6 +104,7 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeRead import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket; import com.android.internal.util.ArrayUtils; @@ -10842,6 +10843,10 @@ public class BatteryStatsImpl extends BatteryStats { mSystemServerCpuThreadReader.startTrackingThreadCpuTime(); } + public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() { + return mSystemServerCpuThreadReader.readAbsolute(); + } + public void setCallback(BatteryCallback cb) { mCallback = cb; } diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java index 141363fa8b25..7c45cc09e18c 100644 --- a/core/java/com/android/internal/os/PowerCalculator.java +++ b/core/java/com/android/internal/os/PowerCalculator.java @@ -143,7 +143,11 @@ public abstract class PowerCalculator { return String.format(Locale.ENGLISH, format, power); } - static double mAhToUJ(long energyUJ) { + static double uJtoMah(long energyUJ) { + if (energyUJ == 0) { + return 0; + } + // TODO(b/173765509): Convert properly. This is mJ / V * (h/3600s) = mAh with V = 3.7 fixed. // Leaving for later since desired units of energy have yet to be decided return energyUJ / 1000.0 / 3.7 / 3600; diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index bb1222eac11c..c1dd7ce662f7 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -21,7 +21,7 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.SystemBatteryConsumer; -import android.os.SystemClock; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.text.format.DateUtils; import android.util.Slog; @@ -39,9 +39,17 @@ public class ScreenPowerCalculator extends PowerCalculator { private static final String TAG = "ScreenPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; + // Minimum amount of time the screen should be on to start smearing drain to apps + public static final long MIN_ACTIVE_TIME_FOR_SMEARING = 10 * DateUtils.MINUTE_IN_MILLIS; + private final UsageBasedPowerEstimator mScreenOnPowerEstimator; private final UsageBasedPowerEstimator mScreenFullPowerEstimator; + private static class PowerAndDuration { + public long durationMs; + public double powerMah; + } + public ScreenPowerCalculator(PowerProfile powerProfile) { mScreenOnPowerEstimator = new UsageBasedPowerEstimator( powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON)); @@ -52,17 +60,40 @@ public class ScreenPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - final long durationMs = computeDuration(batteryStats, rawRealtimeUs, - BatteryStats.STATS_SINCE_CHARGED); - final double powerMah = computePower(batteryStats, rawRealtimeUs, - BatteryStats.STATS_SINCE_CHARGED, durationMs); - if (powerMah != 0) { - builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); + final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); + final boolean forceUsePowerProfileModel = (query.getFlags() + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0; + + final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration, + batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, + forceUsePowerProfileModel); + + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, + totalPowerAndDuration.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, + totalPowerAndDuration.powerMah); + + // Now deal with each app's UidBatteryConsumer. The results are stored in the + // BatteryConsumer.POWER_COMPONENT_SCREEN power component, which is considered smeared, + // but the method depends on the data source. + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + if (useEnergyData) { + final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(), + rawRealtimeUs); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, + appPowerAndDuration.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, + appPowerAndDuration.powerMah); + } + } else { + smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration, + rawRealtimeUs); } - // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats. - // TODO(b/178140704): Attribute (measured/smeared) usage *per app* for BatteryUsageStats. } /** @@ -71,51 +102,79 @@ public class ScreenPowerCalculator extends PowerCalculator { @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - - final long energyUJ = batteryStats.getScreenOnEnergy(); - final boolean isMeasuredDataAvailable = energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE; - - final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType); - final double powerMah = getMeasuredOrComputedPower( - energyUJ, batteryStats, rawRealtimeUs, statsType, durationMs); - if (powerMah == 0) { + final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); + final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration, + batteryStats, rawRealtimeUs, statsType, false); + if (totalPowerAndDuration.powerMah == 0) { return; } // First deal with the SCREEN BatterySipper (since we need this for smearing over apps). final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0); - bs.usagePowerMah = powerMah; - bs.usageTimeMs = durationMs; + bs.usagePowerMah = totalPowerAndDuration.powerMah; + bs.usageTimeMs = totalPowerAndDuration.durationMs; bs.sumPower(); sippers.add(bs); // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah // field, which is considered smeared, but the method depends on the data source. - if (isMeasuredDataAvailable) { - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + if (useEnergyData) { + final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.uidObj, rawRealtimeUs); + app.screenPowerMah = appPowerAndDuration.powerMah; + } + } } else { - smearScreenBatterySipper(sippers, bs); + smearScreenBatterySipper(sippers, bs, rawRealtimeUs); } } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + /** + * Stores duration and power information in totalPowerAndDuration and returns true if measured + * energy data is available and should be used by the model. + */ + private boolean calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, + BatteryStats batteryStats, long rawRealtimeUs, int statsType, + boolean forceUsePowerProfileModel) { + totalPowerAndDuration.durationMs = calculateDuration(batteryStats, rawRealtimeUs, + statsType); + + if (!forceUsePowerProfileModel) { + final long energyUJ = batteryStats.getScreenOnEnergy(); + if (energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + totalPowerAndDuration.powerMah = uJtoMah(energyUJ); + return true; + } + } + + totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats, + rawRealtimeUs, statsType, totalPowerAndDuration.durationMs); + return false; + } + + private void calculateAppUsingMeasuredEnergy(PowerAndDuration appPowerAndDuration, + BatteryStats.Uid u, long rawRealtimeUs) { + appPowerAndDuration.durationMs = getProcessForegroundTimeMs(u, rawRealtimeUs); + final long energyUJ = u.getScreenOnEnergy(); if (energyUJ < 0) { Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called"); + appPowerAndDuration.powerMah = 0; return; } - if (energyUJ == 0) return; - app.screenPowerMah = mAhToUJ(u.getScreenOnEnergy()); + + appPowerAndDuration.powerMah = uJtoMah(energyUJ); } - private long computeDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { + private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000; } - private double computePower(BatteryStats batteryStats, long rawRealtimeUs, int statsType, - long durationMs) { + private double calculateTotalPowerFromBrightness(BatteryStats batteryStats, long rawRealtimeUs, + int statsType, long durationMs) { double power = mScreenOnPowerEstimator.calculatePower(durationMs); for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { final long brightnessTime = @@ -131,35 +190,25 @@ public class ScreenPowerCalculator extends PowerCalculator { return power; } - private double getMeasuredOrComputedPower(long measuredEnergyUJ, - BatteryStats batteryStats, long rawRealtimeUs, int statsType, long durationMs) { - - if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - return mAhToUJ(measuredEnergyUJ); - } else { - return computePower(batteryStats, rawRealtimeUs, statsType, durationMs); - } - } - /** * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity * time, and store this in the {@link BatterySipper#screenPowerMah} field. */ @VisibleForTesting - public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { - + public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper, + long rawRealtimeUs) { long totalActivityTimeMs = 0; final SparseLongArray activityTimeArray = new SparseLongArray(); for (int i = sippers.size() - 1; i >= 0; i--) { final BatteryStats.Uid uid = sippers.get(i).uidObj; if (uid != null) { - final long timeMs = getProcessForegroundTimeMs(uid); + final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs); activityTimeArray.put(uid.getUid(), timeMs); totalActivityTimeMs += timeMs; } } - if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { + if (screenSipper != null && totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) { final double totalScreenPowerMah = screenSipper.totalPowerMah; for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper sipper = sippers.get(i); @@ -170,10 +219,37 @@ public class ScreenPowerCalculator extends PowerCalculator { } } + /** + * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity + * time, and store this in the {@link BatterySipper#screenPowerMah} field. + */ + private void smearScreenBatteryDrain( + SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders, + PowerAndDuration totalPowerAndDuration, long rawRealtimeUs) { + long totalActivityTimeMs = 0; + final SparseLongArray activityTimeArray = new SparseLongArray(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final BatteryStats.Uid uid = uidBatteryConsumerBuilders.valueAt(i).getBatteryStatsUid(); + final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs); + activityTimeArray.put(uid.getUid(), timeMs); + totalActivityTimeMs += timeMs; + } + + if (totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) { + final double totalScreenPowerMah = totalPowerAndDuration.powerMah; + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + final long durationMs = activityTimeArray.get(app.getUid(), 0); + final double powerMah = totalScreenPowerMah * durationMs / totalActivityTimeMs; + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah); + } + } + } + /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */ @VisibleForTesting - public long getProcessForegroundTimeMs(BatteryStats.Uid uid) { - final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000; + public long getProcessForegroundTimeMs(BatteryStats.Uid uid, long rawRealTimeUs) { final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP}; long timeUs = 0; diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java index fbad75e93a17..3ed59f1d4370 100644 --- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -110,4 +110,24 @@ public class SystemServerCpuThreadReader { return mDeltaCpuThreadTimes; } + + /** Returns CPU times, per thread group, since tracking started. */ + @Nullable + public SystemServiceCpuThreadTimes readAbsolute() { + final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount(); + final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = + mKernelCpuThreadReader.getProcessCpuUsage(); + if (processCpuUsage == null) { + return null; + } + final SystemServiceCpuThreadTimes result = new SystemServiceCpuThreadTimes(); + result.threadCpuTimesUs = new long[numCpuFrequencies]; + result.binderThreadCpuTimesUs = new long[numCpuFrequencies]; + for (int i = 0; i < numCpuFrequencies; ++i) { + result.threadCpuTimesUs[i] = processCpuUsage.threadCpuTimesMillis[i] * 1_000; + result.binderThreadCpuTimesUs[i] = + processCpuUsage.selectedThreadCpuTimesMillis[i] * 1_000; + } + return result; + } } diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java index 488b18db66d6..c6a040c162ab 100644 --- a/core/java/com/android/internal/util/CollectionUtils.java +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -47,6 +47,13 @@ public class CollectionUtils { private CollectionUtils() { /* cannot be instantiated */ } /** + * @see Collection#contains(Object) + */ + public static <T> boolean contains(@Nullable Collection<T> collection, T element) { + return collection != null && collection.contains(element); + } + + /** * Returns a list of items from the provided list that match the given condition. * * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index f1998a583a21..c7439f1b32d4 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -188,7 +188,7 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str()); auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider); - std::unique_ptr<const ApkAssets> apk_assets; + std::unique_ptr<ApkAssets> apk_assets; switch (format) { case FORMAT_APK: { auto assets = MultiAssetsProvider::Create(std::move(loader_assets), @@ -354,7 +354,7 @@ static void NativeDestroy(void* ptr) { } static jlong NativeGetFinalizer(JNIEnv* /*env*/, jclass /*clazz*/) { - return reinterpret_cast<jlong>(&NativeDestroy); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&NativeDestroy)); } static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index ac680f6e2611..9746a07a1b77 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -199,7 +199,13 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { for (;;) { uint32_t publishedSeq; bool handled; - status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled); + std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback = + [&publishedSeq, &handled](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + publishedSeq = inSeq; + handled = inHandled; + }; + status_t status = mInputPublisher.receiveFinishedSignal(callback); if (status) { if (status == WOULD_BLOCK) { return OK; diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp index 191f748d80bf..0aac07d17cdc 100644 --- a/core/jni/android_view_SurfaceSession.cpp +++ b/core/jni/android_view_SurfaceSession.cpp @@ -51,19 +51,12 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { client->decStrong((void*)nativeCreate); } -static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) { - SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr); - client->dispose(); -} - static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "nativeCreate", "()J", (void*)nativeCreate }, { "nativeDestroy", "(J)V", (void*)nativeDestroy }, - { "nativeKill", "(J)V", - (void*)nativeKill } }; int register_android_view_SurfaceSession(JNIEnv* env) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 856657ad8b56..99014c5442b0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1055,6 +1055,14 @@ android:description="@string/permdesc_accessImsCallService" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows an application to perform IMS Single Registration related actions. + Only granted if the application is a system app AND is in the Default SMS Role. + The permission is revoked when the app is taken out of the Default SMS Role. + <p>Protection level: internal|role + --> + <permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION" + android:protectionLevel="internal|role" /> + <!-- Allows an application to read the user's call log. <p class="note"><strong>Note:</strong> If your app uses the {@link #READ_CONTACTS} permission and <em>both</em> your <a @@ -3994,6 +4002,11 @@ <permission android:name="android.permission.MOVE_PACKAGE" android:protectionLevel="signature|privileged" /> + <!-- @TestApi Allows an application to keep uninstalled packages as apks. + @hide --> + <permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" + android:protectionLevel="signature" /> + <!-- Allows an application to change whether an application component (other than its own) is enabled or not. <p>Not for use by third-party applications. --> diff --git a/core/res/res/color/btn_leanback_color.xml b/core/res/res/color/btn_leanback_color.xml new file mode 100644 index 000000000000..012df6069e9c --- /dev/null +++ b/core/res/res/color/btn_leanback_color.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_focused="true" + android:color="@color/btn_leanback_focused" /> + <item android:state_enabled="false" + android:color="@android:color/transparent" /> + <item android:color="@color/btn_leanback_unfocused" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color/btn_leanback_text_color.xml b/core/res/res/color/btn_leanback_text_color.xml new file mode 100644 index 000000000000..df0df948063a --- /dev/null +++ b/core/res/res/color/btn_leanback_text_color.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@color/btn_text_leanback_focused" android:state_focused="true"/> + <item android:color="@color/btn_text_leanback_unfocused"/> +</selector> diff --git a/core/res/res/drawable/btn_leanback.xml b/core/res/res/drawable/btn_leanback.xml new file mode 100644 index 000000000000..9a63986b69d4 --- /dev/null +++ b/core/res/res/drawable/btn_leanback.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> + <solid android:color="@color/btn_leanback_color"/> + <corners android:radius="@dimen/leanback_button_radius"/> +</shape> diff --git a/core/res/res/layout/alert_dialog_button_bar_leanback.xml b/core/res/res/layout/alert_dialog_button_bar_leanback.xml new file mode 100644 index 000000000000..ea94af662dcf --- /dev/null +++ b/core/res/res/layout/alert_dialog_button_bar_leanback.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/buttonPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scrollbarAlwaysDrawVerticalTrack="true" + android:scrollIndicators="top|bottom" + android:fillViewport="true" + style="?attr/buttonBarStyle"> + <com.android.internal.widget.ButtonBarLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layoutDirection="locale" + android:orientation="horizontal" + android:gravity="start"> + + <Button + android:id="@+id/button1" + style="?attr/buttonBarPositiveButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button + android:id="@+id/button2" + style="?attr/buttonBarNegativeButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Space + android:id="@+id/spacer" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1" + android:visibility="invisible" /> + + <Button + android:id="@+id/button3" + style="?attr/buttonBarNeutralButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + </com.android.internal.widget.ButtonBarLayout> +</ScrollView> diff --git a/core/res/res/layout/alert_dialog_leanback.xml b/core/res/res/layout/alert_dialog_leanback.xml index 848015c28f4a..091613fd9b56 100644 --- a/core/res/res/layout/alert_dialog_leanback.xml +++ b/core/res/res/layout/alert_dialog_leanback.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2014 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,108 +15,70 @@ limitations under the License. --> -<LinearLayout +<com.android.internal.widget.AlertDialogLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parentPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:background="@drawable/dialog_background_material" - android:translationZ="@dimen/floating_window_z" - android:layout_marginLeft="@dimen/leanback_alert_dialog_horizontal_margin" - android:layout_marginTop="@dimen/leanback_alert_dialog_vertical_margin" - android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin" - android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin"> + android:gravity="start|top" + android:orientation="vertical"> - <LinearLayout android:id="@+id/topPanel" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - <LinearLayout android:id="@+id/title_template" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:gravity="center_vertical|start" - android:paddingStart="16dip" - android:paddingEnd="16dip" - android:paddingTop="16dip"> - <ImageView android:id="@+id/icon" - android:layout_width="32dip" - android:layout_height="32dip" - android:layout_marginEnd="8dip" - android:scaleType="fitCenter" - android:src="@null" /> - <TextView android:id="@+id/alertTitle" - style="?attr/windowTitleStyle" - android:singleLine="true" - android:ellipsize="end" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAlignment="viewStart" /> - </LinearLayout> - <!-- If the client uses a customTitle, it will be added here. --> - </LinearLayout> + <include layout="@layout/alert_dialog_title_material" /> - <LinearLayout android:id="@+id/contentPanel" + <FrameLayout + android:id="@+id/contentPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" - android:orientation="vertical" - android:minHeight="64dp"> - <ScrollView android:id="@+id/scrollView" + android:minHeight="48dp"> + + <ScrollView + android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false"> - <TextView android:id="@+id/message" - style="?attr/textAppearanceMedium" + + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="16dip" - android:paddingEnd="16dip" - android:paddingTop="16dip" /> + android:orientation="vertical"> + + <Space + android:id="@+id/textSpacerNoTitle" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="@dimen/dialog_padding_top_material" /> + + <TextView + android:id="@+id/message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingEnd="?attr/dialogPreferredPadding" + android:paddingStart="?attr/dialogPreferredPadding" + style="@style/TextAppearance.Material.Subhead" /> + + <Space + android:id="@+id/textSpacerNoButtons" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="@dimen/dialog_padding_top_material" /> + </LinearLayout> </ScrollView> - </LinearLayout> + </FrameLayout> - <FrameLayout android:id="@+id/customPanel" + <FrameLayout + android:id="@+id/customPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" - android:minHeight="64dp"> - <FrameLayout android:id="@+android:id/custom" + android:minHeight="48dp"> + + <FrameLayout + android:id="@+id/custom" android:layout_width="match_parent" android:layout_height="wrap_content" /> </FrameLayout> - <LinearLayout android:id="@+id/buttonPanel" + <include android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="@dimen/alert_dialog_button_bar_height" - android:orientation="vertical" - android:gravity="end" - android:padding="16dip"> - <LinearLayout - style="?attr/buttonBarStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layoutDirection="locale"> - <Button android:id="@+id/button3" - style="?attr/buttonBarButtonStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" /> - <Button android:id="@+id/button2" - style="?attr/buttonBarButtonStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" /> - <Button android:id="@+id/button1" - style="?attr/buttonBarButtonStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" /> - </LinearLayout> - </LinearLayout> -</LinearLayout> + layout="@layout/alert_dialog_button_bar_leanback" /> +</com.android.internal.widget.AlertDialogLayout> diff --git a/core/res/res/values-television/styles_device_defaults.xml b/core/res/res/values-television/styles_device_defaults.xml new file mode 100644 index 000000000000..ef55fc68001f --- /dev/null +++ b/core/res/res/values-television/styles_device_defaults.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog" + parent="Widget.Leanback.Button.ButtonBar" /> +</resources> + diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 586c99d76335..aeb4fc468fe4 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1140,6 +1140,15 @@ <!-- The color applied to the edge effect on scrolling containers. --> <attr name="colorEdgeEffect" format="color" /> + <!-- The type of the edge effect. The default is glow. --> + <attr name="edgeEffectType"> + <!-- Use a colored glow at the edge. --> + <enum name="glow" value="0" /> + + <!-- Stretch the content. --> + <enum name="stretch" value="1" /> + </attr> + <!-- =================== --> <!-- Lighting properties --> <!-- =================== --> @@ -9050,6 +9059,7 @@ <!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. --> <declare-styleable name="EdgeEffect"> <attr name="colorEdgeEffect" /> + <attr name="edgeEffectType" /> </declare-styleable> <!-- Use <code>tv-input</code> as the root tag of the XML resource that describes a diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index b4e580aac959..0ae6a76e2a60 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -311,6 +311,10 @@ <flag name="recents" value="0x2000000" /> <!-- Additional flag from base permission type: this permission is managed by role. --> <flag name="role" value="0x4000000" /> + <!-- Additional flag from base permission type: this permission can also be granted if the + requesting application is signed by, or has in its signing lineage, any of the + certificate digests declared in {@link android.R.attr#knownCerts}. --> + <flag name="knownSigner" value="0x8000000" /> </attr> <!-- Flags indicating more context for a permission group. --> @@ -364,6 +368,15 @@ {@link android.R.styleable#AndroidManifestPermissionGroup permission-group} tag. --> <attr name="permissionGroup" format="string" /> + <!-- A reference to an array resource containing the signing certificate digests to be granted + this permission when using the {@code knownSigner} protection flag. The digest should + be computed over the DER encoding of the trusted certificate using the SHA-256 digest + algorithm. + <p> + If only a single signer is declared this can also be a string resource, or the digest + can be declared inline as the value for this attribute. --> + <attr name="knownCerts" format="reference|string" /> + <!-- Specify the name of a user ID that will be shared between multiple packages. By default, each package gets its own unique user-id. By setting this value on two or more packages, each of these packages @@ -1935,6 +1948,7 @@ <attr name="request" /> <attr name="protectionLevel" /> <attr name="permissionFlags" /> + <attr name="knownCerts" /> </declare-styleable> <!-- The <code>permission-group</code> tag declares a logical grouping of diff --git a/core/res/res/values/colors_leanback.xml b/core/res/res/values/colors_leanback.xml index e52a861e2040..df0be03d6f8e 100644 --- a/core/res/res/values/colors_leanback.xml +++ b/core/res/res/values/colors_leanback.xml @@ -26,4 +26,9 @@ <color name="secondary_text_leanback_light">#99222222</color> <color name="primary_text_leanback_formwizard_default_dark">#ffeeeeee</color> + + <color name="btn_leanback_focused">#E8EAED</color> + <color name="btn_leanback_unfocused">#1AFFFFFF</color> + <color name="btn_text_leanback_focused">#0E0E0E</color> + <color name="btn_text_leanback_unfocused">#E8EAED</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index beae9353a10f..09ca12aa9744 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1946,8 +1946,6 @@ <string name="config_systemAutomotiveProjection" translatable="false"></string> <!-- The name of the package that will hold the system cluster service role. --> <string name="config_systemAutomotiveCluster" translatable="false"></string> - <!-- The name of the package that will hold the system video call role. --> - <string name="config_systemVideoCall" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 3b6e41ffa92b..debdab0b37ba 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -394,10 +394,6 @@ <dimen name="alert_dialog_button_bar_width">64dp</dimen> <!-- Dialog button bar height --> <dimen name="alert_dialog_button_bar_height">48dip</dimen> - <!-- Leanback dialog vertical margin --> - <dimen name="leanback_alert_dialog_vertical_margin">27dip</dimen> - <!-- Leanback dialog horizontal margin --> - <dimen name="leanback_alert_dialog_horizontal_margin">54dip</dimen> <!-- Default height of an action bar. --> <dimen name="action_bar_default_height">48dip</dimen> diff --git a/core/res/res/values/dimens_leanback.xml b/core/res/res/values/dimens_leanback.xml index c824a2a79f83..3ab21963ed77 100644 --- a/core/res/res/values/dimens_leanback.xml +++ b/core/res/res/values/dimens_leanback.xml @@ -83,4 +83,10 @@ <dimen name="leanback_setup_translation_backward_out_content_end_v4">@integer/leanback_setup_translation_content_cliff_v4</dimen> <integer name="leanback_setup_translation_backward_out_content_delay">0</integer> <integer name="leanback_setup_translation_backward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer> + + <!-- Button dimens --> + <dimen name="leanback_button_height">42dp</dimen> + <dimen name="leanback_button_radius">55dp</dimen> + <dimen name="leanback_button_padding_horizontal">22dp</dimen> + <dimen name="leanback_button_padding_vertical">11dp</dimen> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index d3f3ebd7c3d9..068987ec8afe 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3063,6 +3063,8 @@ <public name="hotwordDetectionService" /> <public name="previewLayout" /> <public name="clipToOutline" /> + <public name="edgeEffectType" /> + <public name="knownCerts" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> @@ -3117,8 +3119,6 @@ <!-- @hide @SystemApi @TestApi --> <public name="config_systemAutomotiveCluster" /> <!-- @hide @SystemApi @TestApi --> - <public name="config_systemVideoCall" /> - <!-- @hide @SystemApi @TestApi --> <public name="config_systemAutomotiveProjection" /> </public-group> diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml index aaeaadd7418d..7eaf36d5366a 100644 --- a/core/res/res/values/styles_leanback.xml +++ b/core/res/res/values/styles_leanback.xml @@ -16,11 +16,34 @@ <resources> <style name="AlertDialog.Leanback" parent="AlertDialog.Material"> <item name="buttonPanelSideLayout">@android:layout/alert_dialog_leanback_button_panel_side</item> + <item name="layout">@android:layout/alert_dialog_leanback</item> </style> <style name="AlertDialog.Leanback.Light"> </style> + <style name="Widget.Leanback.Button" parent="Widget.Material.Button"> + <item name="android:background">@drawable/btn_leanback</item> + <item name="android:textColor">@color/btn_leanback_text_color</item> + <item name="android:layout_height">@dimen/leanback_button_height</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:textAllCaps">false</item> + <item name="android:paddingHorizontal">@dimen/leanback_button_padding_horizontal</item> + <item name="android:paddingVertical">@dimen/leanback_button_padding_vertical</item> + </style> + + <style name="Widget.Leanback.Button.ButtonBar" parent="Widget.Leanback.Button"> + <item name="android:layout_marginStart">10dp</item> + </style> + + <style name="Widget.Leanback.Button.ButtonBarGravityStart" parent="Widget.Leanback.Button"> + <item name="android:layout_marginEnd">10dp</item> + </style> + + <style name="Widget.Leanback.ButtonBar" parent="Widget.Material.ButtonBar"> + <item name="android:padding">?android:attr/dialogPreferredPadding</item> + </style> + <style name="Widget.Leanback.TimePicker" parent="Widget.Material.TimePicker"> <item name="timePickerMode">spinner</item> </style> diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml index 9dca9128e362..efe582612982 100644 --- a/core/res/res/values/themes_leanback.xml +++ b/core/res/res/values/themes_leanback.xml @@ -22,6 +22,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Light.Dialog" parent="Theme.Material.Light.BaseDialog"> @@ -32,6 +34,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog"> @@ -42,6 +46,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert"> @@ -52,6 +58,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert"> @@ -62,6 +70,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert"> @@ -72,6 +82,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Dialog.AppError" parent="Theme.Leanback.Dialog"> diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java index bffd1e4a86d6..49b720cfba07 100644 --- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java @@ -29,6 +29,7 @@ import static org.junit.Assert.fail; import android.content.pm.PackageParser.SigningDetails; import android.util.ArraySet; +import android.util.PackageUtils; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -817,6 +818,124 @@ public class SigningDetailsTest { assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); } + @Test + public void hasAncestorOrSelfWithDigest_nullSet_returnsFalse() throws Exception { + // The hasAncestorOrSelfWithDigest method is intended to verify whether the SigningDetails + // is currently signed, or has previously been signed, by any of the certificate digests + // in the provided Set. This test verifies if a null Set is provided then false is returned. + SigningDetails details = createSigningDetails(FIRST_SIGNATURE); + + assertFalse(details.hasAncestorOrSelfWithDigest(null)); + } + + @Test + public void hasAncestorOrSelfWithDigest_unknownDetails_returnsFalse() throws Exception { + // If hasAncestorOrSelfWithDigest is invoked against an UNKNOWN + // instance of the SigningDetails then false is returned. + SigningDetails details = SigningDetails.UNKNOWN; + Set<String> digests = createDigestSet(FIRST_SIGNATURE); + + assertFalse(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_singleSignerInSet_returnsTrue() throws Exception { + // If the single signer of an app is in the provided digest Set then + // the method should return true. + SigningDetails details = createSigningDetails(FIRST_SIGNATURE); + Set<String> digests = createDigestSet(FIRST_SIGNATURE, SECOND_SIGNATURE); + + assertTrue(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_singleSignerNotInSet_returnsFalse() throws Exception { + // If the single signer of an app is not in the provided digest Set then + // the method should return false. + SigningDetails details = createSigningDetails(FIRST_SIGNATURE); + Set<String> digests = createDigestSet(SECOND_SIGNATURE, THIRD_SIGNATURE); + + assertFalse(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_multipleSignersInSet_returnsTrue() throws Exception { + // If an app is signed by multiple signers and all of the signers are in + // the digest Set then the method should return true. + SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + Set<String> digests = createDigestSet(FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); + + assertTrue(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_multipleSignersNotInSet_returnsFalse() + throws Exception { + // If an app is signed by multiple signers then all signers must be in the digest Set; if + // only a subset of the signers are in the Set then the method should return false. + SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + Set<String> digests = createDigestSet(FIRST_SIGNATURE, THIRD_SIGNATURE); + + assertFalse(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_multipleSignersOneInSet_returnsFalse() + throws Exception { + // If an app is signed by multiple signers and the Set size is smaller than the number of + // signers then the method should immediately return false since there's no way for the + // requirement of all signers in the Set to be met. + SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + Set<String> digests = createDigestSet(FIRST_SIGNATURE); + + assertFalse(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_lineageSignerInSet_returnsTrue() throws Exception { + // If an app has a rotated signing key and a previous key in the lineage is in the digest + // Set then this method should return true. + SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE); + Set<String> digests = createDigestSet(FIRST_SIGNATURE, THIRD_SIGNATURE); + + assertTrue(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_lineageSignerNotInSet_returnsFalse() throws Exception { + // If an app has a rotated signing key, but neither the current key nor any of the signers + // in the lineage are in the digest set then the method should return false. + SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE); + Set<String> digests = createDigestSet(THIRD_SIGNATURE, FOURTH_SIGNATURE); + + assertFalse(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_lastSignerInLineageInSet_returnsTrue() + throws Exception { + // If an app has multiple signers in the lineage only one of those signers must be in the + // Set for this method to return true. This test verifies if the last signer in the lineage + // is in the set then the method returns true. + SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, + THIRD_SIGNATURE); + Set<String> digests = createDigestSet(SECOND_SIGNATURE); + + assertTrue(details.hasAncestorOrSelfWithDigest(digests)); + } + + @Test + public void hasAncestorOrSelfWithDigest_nullLineageSingleSIgner_returnsFalse() + throws Exception { + // Under some instances an app with only a single signer can have a null lineage; this + // test verifies that null lineage does not result in a NullPointerException and instead the + // method returns false if the single signer is not in the Set. + SigningDetails details = createSigningDetails(true, FIRST_SIGNATURE); + Set<String> digests = createDigestSet(SECOND_SIGNATURE, THIRD_SIGNATURE); + + assertFalse(details.hasAncestorOrSelfWithDigest(digests)); + } + private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception { int[] capabilities = new int[signers.length]; for (int i = 0; i < capabilities.length; i++) { @@ -853,12 +972,21 @@ public class SigningDetailsTest { // If there are multiple signers then the pastSigningCertificates should be set to null, but // if there is only a single signer both the current signer and the past signers should be // set to that one signer. - if (signers.length > 1) { + if (signers.length > 1 || useNullPastSigners) { return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null); } return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures); } + private Set<String> createDigestSet(String... signers) { + Set<String> digests = new ArraySet<>(); + for (String signer : signers) { + String digest = PackageUtils.computeSha256Digest(new Signature(signer).toByteArray()); + digests.add(digest); + } + return digests; + } + private void assertSigningDetailsContainsLineage(SigningDetails details, String... pastSigners) { // This method should only be invoked for results that contain a single signer. diff --git a/core/tests/coretests/src/android/provider/OWNERS b/core/tests/coretests/src/android/provider/OWNERS new file mode 100644 index 000000000000..581da714f326 --- /dev/null +++ b/core/tests/coretests/src/android/provider/OWNERS @@ -0,0 +1 @@ +include /core/java/android/provider/OWNERS diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index d2107eaefefc..08205b4d143c 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java @@ -218,7 +218,7 @@ public class BatteryStatsHelperTest extends TestCase { sippers.add(sipperBg); sippers.add(sipperFg); - spc.smearScreenBatterySipper(sippers, mScreenBatterySipper); + spc.smearScreenBatterySipper(sippers, mScreenBatterySipper, 0); assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0); assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0); @@ -253,7 +253,7 @@ public class BatteryStatsHelperTest extends TestCase { doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(), anyInt()); - final long time = spc.getProcessForegroundTimeMs(mUid); + final long time = spc.getProcessForegroundTimeMs(mUid, 1000); assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS); } @@ -296,7 +296,7 @@ public class BatteryStatsHelperTest extends TestCase { doReturn(uidCode).when(sipper).getUid(); if (!isUidNull) { final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS); - doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid)); + doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid), anyLong()); doReturn(uidCode).when(uid).getUid(); sipper.uidObj = uid; } diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index fdfc7ac5587c..d50bb05c3588 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -232,7 +232,7 @@ public final class LooperStatsTest { assertThat(entry3.handlerClassName).isEqualTo( "com.android.internal.os.LooperStatsTest$TestHandlerSecond"); assertThat(entry3.messageName).startsWith( - "com.android.internal.os.-$$Lambda$LooperStatsTest$"); + "com.android.internal.os.LooperStatsTest-$$ExternalSyntheticLambda"); assertThat(entry3.messageCount).isEqualTo(1); assertThat(entry3.recordedMessageCount).isEqualTo(1); assertThat(entry3.exceptionCount).isEqualTo(0); diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java index e43caa37f711..717fac0c7bf8 100644 --- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -18,8 +18,12 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; +import android.app.ActivityManager; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; +import android.os.Process; import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; import android.view.Display; import androidx.test.filters.SmallTest; @@ -33,20 +37,37 @@ import org.junit.runner.RunWith; @SmallTest public class ScreenPowerCalculatorTest { private static final double PRECISION = 0.00001; + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 43; + private static final long MINUTE_IN_MS = 60 * 1000; + private static final long MINUTE_IN_US = 60 * 1000 * 1000; @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() - .setAveragePower(PowerProfile.POWER_SCREEN_ON, 360.0) - .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 3600.0); + .setAveragePower(PowerProfile.POWER_SCREEN_ON, 36.0) + .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 48.0); @Test - public void testTimerBasedModel() { - BatteryStatsImpl stats = mStatsRule.getBatteryStats(); + public void testEnergyBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); - stats.noteScreenStateLocked(Display.STATE_ON, 1000, 1000, 1000); - stats.noteScreenBrightnessLocked(100, 1000, 1000); - stats.noteScreenBrightnessLocked(200, 2000, 2000); - stats.noteScreenStateLocked(Display.STATE_OFF, 3000, 3000, 3000); + batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); + batteryStats.updateDisplayEnergyLocked(0, Display.STATE_ON, 2 * MINUTE_IN_MS); + + setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS); + setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS); + setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + + batteryStats.updateDisplayEnergyLocked(300_000_000, Display.STATE_ON, + 60 * MINUTE_IN_MS); + + batteryStats.noteScreenStateLocked(Display.STATE_OFF, + 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); + + batteryStats.updateDisplayEnergyLocked(100_000_000, Display.STATE_DOZE, + 120 * MINUTE_IN_MS); + + mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US); ScreenPowerCalculator calculator = new ScreenPowerCalculator(mStatsRule.getPowerProfile()); @@ -56,8 +77,79 @@ public class ScreenPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) - .isEqualTo(2000); + .isEqualTo(80 * MINUTE_IN_MS); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + .isWithin(PRECISION).of(30.03003); + + UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); + assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(18 * MINUTE_IN_MS); + assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(8.44594); + + UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); + assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(90 * MINUTE_IN_MS); + assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(21.58408); + } + + @Test + public void testPowerProfileBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); + + setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS); + + batteryStats.noteScreenBrightnessLocked(100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS); + batteryStats.noteScreenBrightnessLocked(200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + + setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS); + + setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + + batteryStats.noteScreenStateLocked(Display.STATE_OFF, + 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); + + mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US); + + ScreenPowerCalculator calculator = + new ScreenPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); + + SystemBatteryConsumer consumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + .isEqualTo(80 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) - .isWithin(PRECISION).of(1.2); + .isWithin(PRECISION).of(88.4); + + UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); + assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(18 * MINUTE_IN_MS); + assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(14.73333); + + UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); + assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + .isEqualTo(90 * MINUTE_IN_MS); + assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) + .isWithin(PRECISION).of(73.66666); + } + + private void setFgState(int uid, boolean fgOn, long realtimeMs, long uptimeMs) { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + if (fgOn) { + batteryStats.noteActivityResumedLocked(uid, realtimeMs, uptimeMs); + batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_TOP, + realtimeMs, uptimeMs); + } else { + batteryStats.noteActivityPausedLocked(uid, realtimeMs, uptimeMs); + batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY, + realtimeMs, uptimeMs); + } } } diff --git a/core/tests/overlaytests/device/res/values/config.xml b/core/tests/overlaytests/device/res/values/config.xml index e918268fa073..a30d66f82128 100644 --- a/core/tests/overlaytests/device/res/values/config.xml +++ b/core/tests/overlaytests/device/res/values/config.xml @@ -2,6 +2,7 @@ <resources> <string name="str">none</string> <string name="str2">none</string> + <integer name="overlaid">0</integer> <integer name="matrix_100000">100</integer> <integer name="matrix_100001">100</integer> <integer name="matrix_100010">100</integer> diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/FabricatedOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/FabricatedOverlaysTest.java new file mode 100644 index 000000000000..34659897e588 --- /dev/null +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/FabricatedOverlaysTest.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.overlaytest; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertThrows; + +import android.content.Context; +import android.content.om.FabricatedOverlay; +import android.content.om.OverlayIdentifier; +import android.content.om.OverlayInfo; +import android.content.om.OverlayManager; +import android.content.om.OverlayManagerTransaction; +import android.content.res.Resources; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.util.TypedValue; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeoutException; + +@RunWith(JUnit4.class) +@MediumTest +public class FabricatedOverlaysTest { + private static final String TAG = "FabricatedOverlaysTest"; + private final String TEST_RESOURCE = "integer/overlaid"; + private final String TEST_OVERLAY_NAME = "Test"; + + private Context mContext; + private Resources mResources; + private OverlayManager mOverlayManager; + private int mUserId; + private UserHandle mUserHandle; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + mResources = mContext.getResources(); + mOverlayManager = mContext.getSystemService(OverlayManager.class); + mUserId = UserHandle.myUserId(); + mUserHandle = UserHandle.of(mUserId); + } + + @After + public void tearDown() throws Exception { + final OverlayManagerTransaction.Builder cleanUp = new OverlayManagerTransaction.Builder(); + mOverlayManager.getOverlayInfosForTarget(mContext.getPackageName(), mUserHandle).forEach( + info -> { + if (info.isFabricated()) { + cleanUp.unregisterFabricatedOverlay(info.getOverlayIdentifier()); + } + }); + mOverlayManager.commit(cleanUp.build()); + } + + @Test + public void testFabricatedOverlay() throws Exception { + final FabricatedOverlay overlay = new FabricatedOverlay.Builder( + mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + waitForResourceValue(0); + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .build()); + + OverlayInfo info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle); + assertNotNull(info); + assertFalse(info.isEnabled()); + + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .setEnabled(overlay.getIdentifier(), true, mUserId) + .build()); + + info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle); + assertNotNull(info); + assertTrue(info.isEnabled()); + + waitForResourceValue(1); + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .unregisterFabricatedOverlay(overlay.getIdentifier()) + .build()); + + waitForResourceValue(0); + } + + @Test + public void testRegisterEnableAtomic() throws Exception { + final FabricatedOverlay overlay = new FabricatedOverlay.Builder( + mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + waitForResourceValue(0); + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .setEnabled(overlay.getIdentifier(), true, mUserId) + .build()); + + waitForResourceValue(1); + } + + @Test + public void testRegisterTwice() throws Exception { + FabricatedOverlay overlay = new FabricatedOverlay.Builder( + mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + waitForResourceValue(0); + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .setEnabled(overlay.getIdentifier(), true, mUserId) + .build()); + + waitForResourceValue(1); + overlay = new FabricatedOverlay.Builder( + mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 2) + .build(); + + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .build()); + waitForResourceValue(2); + } + + @Test + public void testInvalidOwningPackageName() throws Exception { + final FabricatedOverlay overlay = new FabricatedOverlay.Builder( + "android", TEST_OVERLAY_NAME, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + waitForResourceValue(0); + assertThrows(SecurityException.class, () -> + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .setEnabled(overlay.getIdentifier(), true, mUserId) + .build())); + + assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle)); + } + + @Test + public void testInvalidOverlayName() throws Exception { + final FabricatedOverlay overlay = new FabricatedOverlay.Builder( + mContext.getPackageName(), "invalid@name", mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + waitForResourceValue(0); + assertThrows(SecurityException.class, () -> + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .setEnabled(overlay.getIdentifier(), true, mUserId) + .build())); + + assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle)); + } + + @Test + public void testOverlayIdentifierLongest() throws Exception { + final int maxLength = 255 - 11; // 11 reserved characters + final String longestName = String.join("", + Collections.nCopies(maxLength - mContext.getPackageName().length(), "a")); + { + FabricatedOverlay overlay = new FabricatedOverlay.Builder(mContext.getPackageName(), + longestName, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .build()); + assertNotNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle)); + } + { + FabricatedOverlay overlay = new FabricatedOverlay.Builder(mContext.getPackageName(), + longestName + "a", mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + assertThrows(SecurityException.class, () -> + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .build())); + + assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle)); + } + } + + @Test + public void testInvalidResourceValues() throws Exception { + final FabricatedOverlay overlay = new FabricatedOverlay.Builder( + "android", TEST_OVERLAY_NAME, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .setResourceValue("something", TypedValue.TYPE_INT_DEC, 1) + .build(); + + waitForResourceValue(0); + assertThrows(SecurityException.class, () -> + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .setEnabled(overlay.getIdentifier(), true, mUserId) + .build())); + + assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle)); + } + + @Test + public void testTransactionFailRollback() throws Exception { + final FabricatedOverlay overlay = new FabricatedOverlay.Builder( + mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName()) + .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1) + .build(); + + waitForResourceValue(0); + assertThrows(SecurityException.class, () -> + mOverlayManager.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .setEnabled(overlay.getIdentifier(), true, mUserId) + .setEnabled(new OverlayIdentifier("not-valid"), true, mUserId) + .build())); + + assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle)); + } + + void waitForResourceValue(final int expectedValue) throws TimeoutException { + final long timeOutDuration = 10000; + final long endTime = System.currentTimeMillis() + timeOutDuration; + final String resourceName = TEST_RESOURCE; + final int resourceId = mResources.getIdentifier(resourceName, "", + mContext.getPackageName()); + int resourceValue = 0; + while (System.currentTimeMillis() < endTime) { + resourceValue = mResources.getInteger(resourceId); + if (resourceValue == expectedValue) { + return; + } + } + final String paths = TextUtils.join(",", mResources.getAssets().getApkPaths()); + Log.w(TAG, "current paths: [" + paths + "]", new Throwable()); + throw new TimeoutException("Timed out waiting for '" + resourceName + "' value to equal '" + + expectedValue + "': current value is '" + resourceValue + "'"); + } +} diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java index 76c01a7e1125..3c0c1311a1aa 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java @@ -20,6 +20,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import android.annotation.NonNull; import android.content.Context; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayManager; import android.content.om.OverlayManagerTransaction; import android.os.UserHandle; @@ -32,14 +33,14 @@ import java.util.concurrent.FutureTask; class LocalOverlayManager { private static final long TIMEOUT = 30; - public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable, - @NonNull final String[] overlaysToDisable) throws Exception { + public static void toggleOverlaysAndWait(@NonNull final OverlayIdentifier[] overlaysToEnable, + @NonNull final OverlayIdentifier[] overlaysToDisable) throws Exception { final int userId = UserHandle.myUserId(); OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder(); - for (String pkg : overlaysToEnable) { + for (OverlayIdentifier pkg : overlaysToEnable) { builder.setEnabled(pkg, true, userId); } - for (String pkg : overlaysToDisable) { + for (OverlayIdentifier pkg : overlaysToDisable) { builder.setEnabled(pkg, false, userId); } OverlayManagerTransaction transaction = builder.build(); @@ -48,7 +49,7 @@ class LocalOverlayManager { FutureTask<Boolean> task = new FutureTask<>(() -> { while (true) { final String[] paths = ctx.getResources().getAssets().getApkPaths(); - if (arrayTailContains(paths, overlaysToEnable) + if (arrayTailContainsOverlays(paths, overlaysToEnable) && arrayDoesNotContain(paths, overlaysToDisable)) { return true; } @@ -64,15 +65,15 @@ class LocalOverlayManager { task.get(TIMEOUT, SECONDS); } - private static boolean arrayTailContains(@NonNull final String[] array, - @NonNull final String[] substrings) { - if (array.length < substrings.length) { + private static boolean arrayTailContainsOverlays(@NonNull final String[] array, + @NonNull final OverlayIdentifier[] overlays) { + if (array.length < overlays.length) { return false; } - for (int i = 0; i < substrings.length; i++) { - String a = array[array.length - substrings.length + i]; - String s = substrings[i]; - if (!a.contains(s)) { + for (int i = 0; i < overlays.length; i++) { + String a = array[array.length - overlays.length + i]; + OverlayIdentifier s = overlays[i]; + if (!a.contains(s.getPackageName())) { return false; } } @@ -80,10 +81,10 @@ class LocalOverlayManager { } private static boolean arrayDoesNotContain(@NonNull final String[] array, - @NonNull final String[] substrings) { - for (String s : substrings) { + @NonNull final OverlayIdentifier[] overlays) { + for (OverlayIdentifier s : overlays) { for (String a : array) { - if (a.contains(s)) { + if (a.contains(s.getPackageName())) { return false; } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java index 636f4c8b59ca..8e4b9efafccd 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.Context; +import android.content.om.OverlayIdentifier; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -58,9 +59,12 @@ public abstract class OverlayBaseTest { static final int MODE_SINGLE_OVERLAY = 1; static final int MODE_MULTIPLE_OVERLAYS = 2; - static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one"; - static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two"; - static final String FRAMEWORK_OVERLAY_PKG = "com.android.overlaytest.framework"; + static final OverlayIdentifier APP_OVERLAY_ONE_PKG = + new OverlayIdentifier("com.android.overlaytest.app_overlay_one"); + static final OverlayIdentifier APP_OVERLAY_TWO_PKG = + new OverlayIdentifier("com.android.overlaytest.app_overlay_two"); + static final OverlayIdentifier FRAMEWORK_OVERLAY_PKG = + new OverlayIdentifier("com.android.overlaytest.framework"); protected OverlayBaseTest(int mode) { mMode = mode; diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java index 0b4f5e227169..27d73429f171 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java @@ -16,14 +16,19 @@ package com.android.overlaytest; +import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_ONE_PKG; +import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_TWO_PKG; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.testng.Assert.assertThrows; import android.content.Context; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManager; import android.content.om.OverlayManagerTransaction; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.UserHandle; @@ -40,8 +45,6 @@ import java.util.List; @RunWith(JUnit4.class) @MediumTest public class TransactionTest { - static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one"; - static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two"; private Context mContext; private Resources mResources; @@ -58,8 +61,8 @@ public class TransactionTest { mUserHandle = UserHandle.of(mUserId); LocalOverlayManager.toggleOverlaysAndWait( - new String[]{}, - new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); + new OverlayIdentifier[]{}, + new OverlayIdentifier[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); } @Test @@ -78,8 +81,8 @@ public class TransactionTest { List<OverlayInfo> ois = mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); assertEquals(ois.size(), 2); - assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG); - assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG); + assertEquals(ois.get(0).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG); + assertEquals(ois.get(1).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG); OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder() .setEnabled(APP_OVERLAY_TWO_PKG, true) @@ -92,8 +95,8 @@ public class TransactionTest { List<OverlayInfo> ois2 = mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); assertEquals(ois2.size(), 2); - assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG); - assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG); + assertEquals(ois2.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG); + assertEquals(ois2.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG); OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder() .setEnabled(APP_OVERLAY_TWO_PKG, false) @@ -105,8 +108,8 @@ public class TransactionTest { List<OverlayInfo> ois3 = mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); assertEquals(ois3.size(), 2); - assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG); - assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG); + assertEquals(ois3.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG); + assertEquals(ois3.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG); } @Test @@ -116,7 +119,7 @@ public class TransactionTest { OverlayManagerTransaction t = new OverlayManagerTransaction.Builder() .setEnabled(APP_OVERLAY_ONE_PKG, true) - .setEnabled("does-not-exist", true) + .setEnabled(new OverlayIdentifier("does-not-exist"), true) .setEnabled(APP_OVERLAY_TWO_PKG, true) .build(); assertThrows(SecurityException.class, () -> mOverlayManager.commit(t)); @@ -125,9 +128,10 @@ public class TransactionTest { assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId); } - private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) { - final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId)); + private void assertOverlayIsEnabled(final OverlayIdentifier overlay, boolean enabled, + int userId) { + final OverlayInfo oi = mOverlayManager.getOverlayInfo(overlay, UserHandle.of(userId)); assertNotNull(oi); - assertEquals(oi.isEnabled(), enabled); + assertEquals(enabled, oi.isEnabled()); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java index 420f755c5251..558720368cc6 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java @@ -16,6 +16,8 @@ package com.android.overlaytest; +import android.content.om.OverlayIdentifier; + import androidx.test.filters.MediumTest; import org.junit.BeforeClass; @@ -32,7 +34,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest { @BeforeClass public static void enableOverlay() throws Exception { LocalOverlayManager.toggleOverlaysAndWait( - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}, - new String[]{}); + new OverlayIdentifier[]{ + FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG + }, + new OverlayIdentifier[]{}); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java index a86255e96388..d27543384f6c 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java @@ -16,6 +16,8 @@ package com.android.overlaytest; +import android.content.om.OverlayIdentifier; + import androidx.test.filters.MediumTest; import org.junit.BeforeClass; @@ -32,7 +34,7 @@ public class WithOverlayTest extends OverlayBaseTest { @BeforeClass public static void enableOverlays() throws Exception { LocalOverlayManager.toggleOverlaysAndWait( - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG}, - new String[]{APP_OVERLAY_TWO_PKG}); + new OverlayIdentifier[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG}, + new OverlayIdentifier[]{APP_OVERLAY_TWO_PKG}); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java index 51c411819b87..72cba8b87c8c 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java @@ -16,6 +16,8 @@ package com.android.overlaytest; +import android.content.om.OverlayIdentifier; + import androidx.test.filters.MediumTest; import org.junit.BeforeClass; @@ -32,7 +34,9 @@ public class WithoutOverlayTest extends OverlayBaseTest { @BeforeClass public static void disableOverlays() throws Exception { LocalOverlayManager.toggleOverlaysAndWait( - new String[]{}, - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); + new OverlayIdentifier[]{}, + new OverlayIdentifier[]{ + FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG + }); } } diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml index 38e5fa18300c..926b1864d97c 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml @@ -25,7 +25,6 @@ <item target="integer/matrix_100100" value="@integer/matrix_100100"/> <item target="integer/matrix_100101" value="@integer/matrix_100101"/> <item target="integer/matrix_100110" value="@integer/matrix_100110"/> - <item target="integer/matrix_100110" value="@integer/matrix_100110"/> <item target="integer/matrix_100111" value="@integer/matrix_100111"/> <item target="integer/matrix_101000" value="@integer/matrix_101000"/> <item target="integer/matrix_101001" value="@integer/matrix_101001"/> diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml index cf0199bd1b85..c9405748d7de 100644 --- a/data/etc/car/com.android.car.developeroptions.xml +++ b/data/etc/car/com.android.car.developeroptions.xml @@ -26,6 +26,7 @@ <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.FORCE_STOP_PACKAGES"/> + <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> <permission name="android.permission.MANAGE_DEBUGGING"/> <permission name="android.permission.MANAGE_FINGERPRINT"/> @@ -40,10 +41,12 @@ <permission name="android.permission.MOVE_PACKAGE"/> <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_DREAM_STATE"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.READ_SEARCH_INDEXABLES"/> <permission name="android.permission.REBOOT"/> <permission name="android.permission.REQUEST_NETWORK_SCORES"/> + <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/> <permission name="android.permission.SET_TIME"/> <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.TETHER_PRIVILEGED"/> diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml index 19548bc404d1..48f6ab376c82 100644 --- a/data/etc/car/com.android.car.xml +++ b/data/etc/car/com.android.car.xml @@ -25,6 +25,7 @@ <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.REBOOT"/> <permission name="android.permission.READ_LOGS"/> + <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> </permissions> diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml index 3a20a9cb1695..bd30d7a61517 100644 --- a/data/etc/car/com.google.android.car.kitchensink.xml +++ b/data/etc/car/com.google.android.car.kitchensink.xml @@ -27,8 +27,10 @@ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> <permission name="android.permission.LOCATION_HARDWARE"/> + <permission name="android.permission.LOCK_DEVICE"/> <permission name="android.permission.MANAGE_USB"/> <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MASTER_CLEAR"/> <!-- use for CarServiceTest --> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> @@ -40,9 +42,11 @@ <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.READ_LOGS"/> <permission name="android.permission.REBOOT"/> + <permission name="android.permission.RESET_PASSWORD"/> <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/> <!-- use for CarServiceTest --> <permission name="android.permission.SET_ACTIVITY_WATCHER"/> + <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <!-- use for rotary fragment to enable/disable packages related to rotary --> diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index e473c55ce010..3fdb0da80875 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -58,5 +58,6 @@ <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/> <permission name="android.permission.READ_DREAM_STATE"/> <permission name="android.permission.READ_DREAM_SUPPRESSION"/> + <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/> </privapp-permissions> </permissions> diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java index cf2f970e3d58..25f76f6f6da7 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -17,6 +17,7 @@ package android.graphics; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -96,6 +97,27 @@ public class Point implements Parcelable { } /** + * @return Returns a {@link String} that represents this point which can be parsed with + * {@link #unflattenFromString(String)}. + * @hide + */ + @NonNull + public String flattenToString() { + return x + "x" + y; + } + + /** + * @return Returns a {@link Point} from a short string created from {@link #flattenToString()}. + * @hide + */ + @Nullable + public static Point unflattenFromString(String s) throws NumberFormatException { + final int sep_ix = s.indexOf("x"); + return new Point(Integer.parseInt(s.substring(0, sep_ix)), + Integer.parseInt(s.substring(sep_ix + 1))); + } + + /** * Parcelable interface methods */ @Override diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 35e6b8595c2a..40c75a4d2f2f 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1414,6 +1414,16 @@ public class Typeface { return Arrays.binarySearch(mSupportedAxes, axis) >= 0; } + /** @hide */ + public List<FontFamily> getFallback() { + ArrayList<FontFamily> families = new ArrayList<>(); + int familySize = nativeGetFamilySize(native_instance); + for (int i = 0; i < familySize; ++i) { + families.add(new FontFamily(nativeGetFamily(native_instance, i))); + } + return families; + } + private static native long nativeCreateFromTypeface(long native_instance, int style); private static native long nativeCreateFromTypefaceWithExactStyle( long native_instance, int weight, boolean italic); @@ -1439,6 +1449,13 @@ public class Typeface { @CriticalNative private static native long nativeGetReleaseFunc(); + @CriticalNative + private static native int nativeGetFamilySize(long naitvePtr); + + @CriticalNative + private static native long nativeGetFamily(long nativePtr, int index); + + private static native void nativeRegisterGenericFamily(String str, long nativePtr); private static native int nativeWriteTypefaces( diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index 8c13d3e7e51d..a771a6e35345 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -125,7 +125,7 @@ public final class FontFamily { nAddFont(builderPtr, mFonts.get(i).getNativePtr()); } final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback); - final FontFamily family = new FontFamily(mFonts, ptr); + final FontFamily family = new FontFamily(ptr); sFamilyRegistory.registerNativeAllocation(family, ptr); return family; } @@ -146,7 +146,8 @@ public final class FontFamily { private final long mNativePtr; // Use Builder instead. - private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) { + /** @hide */ + public FontFamily(long ptr) { mNativePtr = ptr; } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 904085feb8cb..255f9e659c36 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -68,44 +68,23 @@ public final class SystemFonts { */ public static @NonNull Set<Font> getAvailableFonts() { synchronized (LOCK) { - if (sAvailableFonts != null) { - return sAvailableFonts; - } - - if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { - sAvailableFonts = collectAllFonts(); - } else { + if (sAvailableFonts == null) { Set<Font> set = new ArraySet<>(); - for (FontFamily[] items : sFamilyMap.values()) { - for (FontFamily family : items) { - for (int i = 0; i < family.getSize(); ++i) { - set.add(family.getFont(i)); + for (Typeface tf : Typeface.getSystemFontMap().values()) { + List<FontFamily> families = tf.getFallback(); + for (int i = 0; i < families.size(); ++i) { + FontFamily family = families.get(i); + for (int j = 0; j < family.getSize(); ++j) { + set.add(family.getFont(j)); } } } - sAvailableFonts = Collections.unmodifiableSet(set); } return sAvailableFonts; } } - private static @NonNull Set<Font> collectAllFonts() { - // TODO: use updated fonts - FontConfig fontConfig = getSystemPreinstalledFontConfig(); - Map<String, FontFamily[]> map = buildSystemFallback(fontConfig); - - Set<Font> res = new ArraySet<>(); - for (FontFamily[] families : map.values()) { - for (FontFamily family : families) { - for (int i = 0; i < family.getSize(); ++i) { - res.add(family.getFont(i)); - } - } - } - return res; - } - private static @Nullable ByteBuffer mmap(@NonNull String fullPath) { try (FileInputStream file = new FileInputStream(fullPath)) { final FileChannel fileChannel = file.getChannel(); @@ -329,13 +308,4 @@ public final class SystemFonts { Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result); return result; } - - /** - * @hide - */ - public void resetFallbackMapping(Map<String, FontFamily[]> fallbackMap) { - synchronized (LOCK) { - sFamilyMap = fallbackMap; - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java index aa82339a436a..73fd6931066d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java @@ -16,24 +16,16 @@ package com.android.wm.shell; -import android.util.Slog; - -import com.android.wm.shell.apppairs.AppPairs; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; -import com.android.wm.shell.onehanded.OneHanded; -import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.common.annotations.ExternalThread; import java.io.PrintWriter; -import java.util.Optional; -import java.util.concurrent.TimeUnit; /** * An entry point into the shell for dumping shell internal state and running adb commands. * * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}. */ +@ExternalThread public interface ShellCommandHandler { /** * Dumps the shell state. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java index 982cc006e331..eaed24d6195a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -18,13 +18,12 @@ package com.android.wm.shell; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; -import com.android.wm.shell.apppairs.AppPairs; +import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; -import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; -import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; +import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.splitscreen.SplitScreenController; import java.io.PrintWriter; @@ -38,24 +37,24 @@ import java.util.Optional; public final class ShellCommandHandlerImpl { private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName(); - private final Optional<LegacySplitScreen> mLegacySplitScreenOptional; + private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional; private final Optional<SplitScreenController> mSplitScreenOptional; private final Optional<Pip> mPipOptional; - private final Optional<OneHanded> mOneHandedOptional; - private final Optional<HideDisplayCutout> mHideDisplayCutout; + private final Optional<OneHandedController> mOneHandedOptional; + private final Optional<HideDisplayCutoutController> mHideDisplayCutout; + private final Optional<AppPairsController> mAppPairsOptional; private final ShellTaskOrganizer mShellTaskOrganizer; - private final Optional<AppPairs> mAppPairsOptional; private final ShellExecutor mMainExecutor; private final HandlerImpl mImpl = new HandlerImpl(); public static ShellCommandHandler create( ShellTaskOrganizer shellTaskOrganizer, - Optional<LegacySplitScreen> legacySplitScreenOptional, + Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, - Optional<OneHanded> oneHandedOptional, - Optional<HideDisplayCutout> hideDisplayCutout, - Optional<AppPairs> appPairsOptional, + Optional<OneHandedController> oneHandedOptional, + Optional<HideDisplayCutoutController> hideDisplayCutout, + Optional<AppPairsController> appPairsOptional, ShellExecutor mainExecutor) { return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout, @@ -64,12 +63,12 @@ public final class ShellCommandHandlerImpl { private ShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, - Optional<LegacySplitScreen> legacySplitScreenOptional, + Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, - Optional<OneHanded> oneHandedOptional, - Optional<HideDisplayCutout> hideDisplayCutout, - Optional<AppPairs> appPairsOptional, + Optional<OneHandedController> oneHandedOptional, + Optional<HideDisplayCutoutController> hideDisplayCutout, + Optional<AppPairsController> appPairsOptional, ShellExecutor mainExecutor) { mShellTaskOrganizer = shellTaskOrganizer; mLegacySplitScreenOptional = legacySplitScreenOptional; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 925bf4bbb01c..7376d9898ab8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -18,13 +18,12 @@ package com.android.wm.shell; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; -import com.android.wm.shell.apppairs.AppPairs; +import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; -import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; @@ -39,9 +38,9 @@ public class ShellInitImpl { private final DisplayImeController mDisplayImeController; private final DragAndDropController mDragAndDropController; private final ShellTaskOrganizer mShellTaskOrganizer; - private final Optional<LegacySplitScreen> mLegacySplitScreenOptional; + private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional; private final Optional<SplitScreenController> mSplitScreenOptional; - private final Optional<AppPairs> mAppPairsOptional; + private final Optional<AppPairsController> mAppPairsOptional; private final FullscreenTaskListener mFullscreenTaskListener; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; @@ -51,9 +50,9 @@ public class ShellInitImpl { public static ShellInit create(DisplayImeController displayImeController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, - Optional<LegacySplitScreen> legacySplitScreenOptional, + Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, - Optional<AppPairs> appPairsOptional, + Optional<AppPairsController> appPairsOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, ShellExecutor mainExecutor) { @@ -71,9 +70,9 @@ public class ShellInitImpl { private ShellInitImpl(DisplayImeController displayImeController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, - Optional<LegacySplitScreen> legacySplitScreenOptional, + Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, - Optional<AppPairs> appPairsOptional, + Optional<AppPairsController> appPairsOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, ShellExecutor mainExecutor) { @@ -97,7 +96,7 @@ public class ShellInitImpl { // Register the shell organizer mShellTaskOrganizer.registerOrganizer(); - mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered); + mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered); mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered); // Bind the splitscreen impl to the drag drop controller diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index a570c0af698d..b22f358c0781 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -44,7 +44,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.sizecompatui.SizeCompatUI; +import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; import java.io.PrintWriter; @@ -108,20 +108,20 @@ public class ShellTaskOrganizer extends TaskOrganizer { * compat. */ @Nullable - private final SizeCompatUI mSizeCompatUI; + private final SizeCompatUIController mSizeCompatUI; public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) { this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */); } public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable - SizeCompatUI sizeCompatUI) { + SizeCompatUIController sizeCompatUI) { this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI); } @VisibleForTesting ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor, - Context context, @Nullable SizeCompatUI sizeCompatUI) { + Context context, @Nullable SizeCompatUIController sizeCompatUI) { super(taskOrganizerController, mainExecutor); // TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled // by a controller, that class should be create while porting @@ -342,8 +342,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { } /** - * Notifies {@link SizeCompatUI} about the size compat info changed on the give Task to update - * the UI accordingly. + * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task + * to update the UI accordingly. * * @param taskInfo the new Task info * @param taskListener listener to handle the Task Surface placement. {@code null} if task is diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java index a5dd79b373bd..58ca1fbaba24 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java @@ -30,6 +30,7 @@ import java.util.function.Consumer; public class TaskViewFactoryController { private final ShellTaskOrganizer mTaskOrganizer; private final ShellExecutor mShellExecutor; + private final TaskViewFactory mImpl = new TaskViewFactoryImpl(); public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer, ShellExecutor shellExecutor) { @@ -37,8 +38,11 @@ public class TaskViewFactoryController { mShellExecutor = shellExecutor; } + public TaskViewFactory asTaskViewFactory() { + return mImpl; + } + /** Creates an {@link TaskView} */ - @ShellMainThread public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) { TaskView taskView = new TaskView(context, mTaskOrganizer); executor.execute(() -> { @@ -46,10 +50,6 @@ public class TaskViewFactoryController { }); } - public TaskViewFactory getTaskViewFactory() { - return new TaskViewFactoryImpl(); - } - private class TaskViewFactoryImpl implements TaskViewFactory { @ExternalThread public void create(@UiContext Context context, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java index abd92577c1d4..59271e9fb63c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java @@ -27,6 +27,7 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener /** * The singleton wrapper to communicate between WindowManagerService and WMShell features * (e.g: PIP, SplitScreen, Bubble, OneHandedMode...etc) + * TODO: Remove once PinnedStackListenerForwarder can be removed */ public class WindowManagerShellWrapper { private static final String TAG = WindowManagerShellWrapper.class.getSimpleName(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java index f5aa852c87ae..a9b1dbc3c23b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java @@ -35,8 +35,4 @@ public interface AppPairs { boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2); /** Unpairs any app-pair containing this task id. */ void unpair(int taskId); - /** Dumps current status of app pairs. */ - void dump(@NonNull PrintWriter pw, String prefix); - /** Called when the shell organizer has been registered. */ - void onOrganizerRegistered(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java index e380426b9ca2..0415f12496f2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java @@ -51,18 +51,7 @@ public class AppPairsController { private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>(); private final DisplayController mDisplayController; - /** - * Creates {@link AppPairs}, returns {@code null} if the feature is not supported. - */ - @Nullable - public static AppPairs create(ShellTaskOrganizer organizer, - SyncTransactionQueue syncQueue, DisplayController displayController, - ShellExecutor mainExecutor) { - return new AppPairsController(organizer, syncQueue, displayController, - mainExecutor).mImpl; - } - - AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, + public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, DisplayController displayController, ShellExecutor mainExecutor) { mTaskOrganizer = organizer; mSyncQueue = syncQueue; @@ -70,18 +59,22 @@ public class AppPairsController { mMainExecutor = mainExecutor; } - void onOrganizerRegistered() { + public AppPairs asAppPairs() { + return mImpl; + } + + public void onOrganizerRegistered() { if (mPairsPool == null) { setPairsPool(new AppPairsPool(this)); } } @VisibleForTesting - void setPairsPool(AppPairsPool pool) { + public void setPairsPool(AppPairsPool pool) { mPairsPool = pool; } - boolean pair(int taskId1, int taskId2) { + public boolean pair(int taskId1, int taskId2) { final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1); final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2); if (task1 == null || task2 == null) { @@ -90,13 +83,13 @@ public class AppPairsController { return pair(task1, task2); } - boolean pair(ActivityManager.RunningTaskInfo task1, + public boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) { return pairInner(task1, task2) != null; } @VisibleForTesting - AppPair pairInner( + public AppPair pairInner( @NonNull ActivityManager.RunningTaskInfo task1, @NonNull ActivityManager.RunningTaskInfo task2) { final AppPair pair = mPairsPool.acquire(); @@ -109,11 +102,11 @@ public class AppPairsController { return pair; } - void unpair(int taskId) { + public void unpair(int taskId) { unpair(taskId, true /* releaseToPool */); } - void unpair(int taskId, boolean releaseToPool) { + public void unpair(int taskId, boolean releaseToPool) { AppPair pair = mActiveAppPairs.get(taskId); if (pair == null) { for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) { @@ -137,19 +130,19 @@ public class AppPairsController { } } - ShellTaskOrganizer getTaskOrganizer() { + public ShellTaskOrganizer getTaskOrganizer() { return mTaskOrganizer; } - SyncTransactionQueue getSyncTransactionQueue() { + public SyncTransactionQueue getSyncTransactionQueue() { return mSyncQueue; } - DisplayController getDisplayController() { + public DisplayController getDisplayController() { return mDisplayController; } - private void dump(@NonNull PrintWriter pw, String prefix) { + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; pw.println(prefix + this); @@ -202,21 +195,5 @@ public class AppPairsController { AppPairsController.this.unpair(taskId); }); } - - @Override - public void onOrganizerRegistered() { - mMainExecutor.execute(() -> { - AppPairsController.this.onOrganizerRegistered(); - }); - } - - @Override - public void dump(@NonNull PrintWriter pw, String prefix) { - try { - mMainExecutor.executeBlocking(() -> AppPairsController.this.dump(pw, prefix)); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to dump AppPairsController in 2s"); - } - } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index d73fc6dca4c6..2391a0874bcb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -192,9 +192,9 @@ public class BubbleController { private boolean mIsStatusBarShade = true; /** - * Injected constructor. + * Creates an instance of the BubbleController. */ - public static Bubbles create(Context context, + public static BubbleController create(Context context, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, FloatingContentCoordinator floatingContentCoordinator, @Nullable IStatusBarService statusBarService, @@ -211,14 +211,14 @@ public class BubbleController { return new BubbleController(context, data, synchronizer, floatingContentCoordinator, new BubbleDataRepository(context, launcherApps, mainExecutor), statusBarService, windowManager, windowManagerShellWrapper, launcherApps, - logger, organizer, positioner, mainExecutor, mainHandler).mImpl; + logger, organizer, positioner, mainExecutor, mainHandler); } /** * Testing constructor. */ @VisibleForTesting - public BubbleController(Context context, + protected BubbleController(Context context, BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, FloatingContentCoordinator floatingContentCoordinator, @@ -322,7 +322,7 @@ public class BubbleController { } @VisibleForTesting - public Bubbles getImpl() { + public Bubbles asBubbles() { return mImpl; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 21004280c952..2f31acd41408 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -163,6 +163,7 @@ public class BubbleExpandedView extends LinearLayout { // Apply flags to make behaviour match documentLaunchMode=always. fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT); fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + fillInIntent.putExtra(Intent.EXTRA_IS_BUBBLED, true); if (mBubble != null) { mBubble.setIntentActive(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 728f60d93497..87f0c25c93df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -39,6 +39,7 @@ import android.view.IWindow; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; import android.view.WindowManager; import android.view.WindowlessWindowManager; @@ -55,6 +56,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { private final ParentContainerCallbacks mParentContainerCallbacks; private Context mContext; private SurfaceControlViewHost mViewHost; + private SurfaceControl mLeash; private boolean mResizingSplits; private final String mWindowName; @@ -88,7 +90,15 @@ public final class SplitWindowManager extends WindowlessWindowManager { @Override protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - mParentContainerCallbacks.attachToParentSurface(b); + // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName(TAG) + .setHidden(false) + .setCallsite("SplitWindowManager#attachToParentSurface"); + mParentContainerCallbacks.attachToParentSurface(builder); + mLeash = builder.build(); + b.setParent(mLeash); } /** Inflates {@link DividerView} on to the root surface. */ @@ -118,9 +128,15 @@ public final class SplitWindowManager extends WindowlessWindowManager { * hierarchy. */ void release() { - if (mViewHost == null) return; - mViewHost.release(); - mViewHost = null; + if (mViewHost != null){ + mViewHost.release(); + mViewHost = null; + } + + if (mLeash != null) { + new SurfaceControl.Transaction().remove(mLeash).apply(); + mLeash = null; + } } void setResizingSplits(boolean resizing) { @@ -139,6 +155,6 @@ public final class SplitWindowManager extends WindowlessWindowManager { */ @Nullable SurfaceControl getSurfaceControl() { - return mViewHost == null ? null : getSurfaceControl(mViewHost.getWindowToken()); + return mLeash; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java index 3a2f0da6bf03..60123ab97fd7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java @@ -31,12 +31,6 @@ import java.io.PrintWriter; public interface HideDisplayCutout { /** * Notifies {@link Configuration} changed. - * @param newConfig */ void onConfigurationChanged(Configuration newConfig); - - /** - * Dumps hide display cutout status. - */ - void dump(@NonNull PrintWriter pw); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java index 12b8b87f1285..23f76ca5f6ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java @@ -44,20 +44,12 @@ public class HideDisplayCutoutController { @VisibleForTesting boolean mEnabled; - HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer, - ShellExecutor mainExecutor) { - mContext = context; - mOrganizer = organizer; - mMainExecutor = mainExecutor; - updateStatus(); - } - /** * Creates {@link HideDisplayCutoutController}, returns {@code null} if the feature is not * supported. */ @Nullable - public static HideDisplayCutout create( + public static HideDisplayCutoutController create( Context context, DisplayController displayController, ShellExecutor mainExecutor) { // The SystemProperty is set for devices that support this feature and is used to control // whether to create the HideDisplayCutout instance. @@ -68,7 +60,19 @@ public class HideDisplayCutoutController { HideDisplayCutoutOrganizer organizer = new HideDisplayCutoutOrganizer(context, displayController, mainExecutor); - return new HideDisplayCutoutController(context, organizer, mainExecutor).mImpl; + return new HideDisplayCutoutController(context, organizer, mainExecutor); + } + + HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer, + ShellExecutor mainExecutor) { + mContext = context; + mOrganizer = organizer; + mMainExecutor = mainExecutor; + updateStatus(); + } + + public HideDisplayCutout asHideDisplayCutout() { + return mImpl; } @VisibleForTesting @@ -94,7 +98,7 @@ public class HideDisplayCutoutController { updateStatus(); } - private void dump(@NonNull PrintWriter pw) { + public void dump(@NonNull PrintWriter pw) { final String prefix = " "; pw.print(TAG); pw.println(" states: "); @@ -111,14 +115,5 @@ public class HideDisplayCutoutController { HideDisplayCutoutController.this.onConfigurationChanged(newConfig); }); } - - @Override - public void dump(@NonNull PrintWriter pw) { - try { - mMainExecutor.executeBlocking(() -> HideDisplayCutoutController.this.dump(pw)); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to dump HideDisplayCutoutController in 2s"); - } - } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java index bca6deb451c9..d25bef197359 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java @@ -115,21 +115,6 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays private volatile boolean mAdjustedForIme = false; private boolean mHomeStackResizable = false; - /** - * Creates {@link SplitScreen}, returns {@code null} if the feature is not supported. - */ - @Nullable - public static LegacySplitScreen create(Context context, - DisplayController displayController, SystemWindows systemWindows, - DisplayImeController imeController, TransactionPool transactionPool, - ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, - TaskStackListenerImpl taskStackListener, Transitions transitions, - ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) { - return new LegacySplitScreenController(context, displayController, systemWindows, - imeController, transactionPool, shellTaskOrganizer, syncQueue, taskStackListener, - transitions, mainExecutor, sfVsyncAnimationHandler).mImpl; - } - public LegacySplitScreenController(Context context, DisplayController displayController, SystemWindows systemWindows, DisplayImeController imeController, TransactionPool transactionPool, @@ -228,8 +213,12 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } }); } + + public LegacySplitScreen asLegacySplitScreen() { + return mImpl; + } - void onSplitScreenSupported() { + public void onSplitScreenSupported() { // Set starting tile bounds based on middle target final WindowContainerTransaction tct = new WindowContainerTransaction(); int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; @@ -237,7 +226,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays mTaskOrganizer.applyTransaction(tct); } - private void onKeyguardVisibilityChanged(boolean showing) { + public void onKeyguardVisibilityChanged(boolean showing) { if (!isSplitActive() || mView == null) { return; } @@ -293,19 +282,19 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - boolean isMinimized() { + public boolean isMinimized() { return mMinimized; } - boolean isHomeStackResizable() { + public boolean isHomeStackResizable() { return mHomeStackResizable; } - DividerView getDividerView() { + public DividerView getDividerView() { return mView; } - boolean isDividerVisible() { + public boolean isDividerVisible() { return mView != null && mView.getVisibility() == View.VISIBLE; } @@ -314,13 +303,13 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays * isDividerVisible because the divider is only visible once *everything* is in split mode * while this only cares if some things are (eg. while entering/exiting as well). */ - private boolean isSplitActive() { + public boolean isSplitActive() { return mSplits.mPrimary != null && mSplits.mSecondary != null && (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED || mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED); } - private void addDivider(Configuration configuration) { + public void addDivider(Configuration configuration) { Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId()); mView = (DividerView) LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null); @@ -338,14 +327,14 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays mWindowManager.add(mView, width, height, mContext.getDisplayId()); } - private void removeDivider() { + public void removeDivider() { if (mView != null) { mView.onDividerRemoved(); } mWindowManager.remove(); } - private void update(Configuration configuration) { + public void update(Configuration configuration) { final boolean isDividerHidden = mView != null && mIsKeyguardShowing; removeDivider(); @@ -358,11 +347,11 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays mView.setHidden(isDividerHidden); } - void onTaskVanished() { + public void onTaskVanished() { removeDivider(); } - private void updateVisibility(final boolean visible) { + public void updateVisibility(final boolean visible) { if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible); if (mVisible != visible) { mVisible = visible; @@ -390,7 +379,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - private void setMinimized(final boolean minimized) { + public void setMinimized(final boolean minimized) { if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible); mMainExecutor.execute(() -> { if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible); @@ -401,7 +390,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays }); } - private void setHomeMinimized(final boolean minimized) { + public void setHomeMinimized(final boolean minimized) { if (DEBUG) { Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:" + mHomeStackResizable + " split:" + isDividerVisible()); @@ -441,7 +430,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - void setAdjustedForIme(boolean adjustedForIme) { + public void setAdjustedForIme(boolean adjustedForIme) { if (mAdjustedForIme == adjustedForIme) { return; } @@ -449,30 +438,30 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays updateTouchable(); } - private void updateTouchable() { + public void updateTouchable() { mWindowManager.setTouchable(!mAdjustedForIme); } - private void onUndockingTask() { + public void onUndockingTask() { if (mView != null) { mView.onUndockingTask(); } } - private void onAppTransitionFinished() { + public void onAppTransitionFinished() { if (mView == null) { return; } mForcedResizableController.onAppTransitionFinished(); } - private void dump(PrintWriter pw) { + public void dump(PrintWriter pw) { pw.print(" mVisible="); pw.println(mVisible); pw.print(" mMinimized="); pw.println(mMinimized); pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme); } - long getAnimDuration() { + public long getAnimDuration() { float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat( @@ -482,14 +471,14 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays return (long) (transitionDuration * transitionScale); } - void registerInSplitScreenListener(Consumer<Boolean> listener) { + public void registerInSplitScreenListener(Consumer<Boolean> listener) { listener.accept(isDividerVisible()); synchronized (mDockedStackExistsListeners) { mDockedStackExistsListeners.add(new WeakReference<>(listener)); } } - void unregisterInSplitScreenListener(Consumer<Boolean> listener) { + public void unregisterInSplitScreenListener(Consumer<Boolean> listener) { synchronized (mDockedStackExistsListeners) { for (int i = mDockedStackExistsListeners.size() - 1; i >= 0; i--) { if (mDockedStackExistsListeners.get(i) == listener) { @@ -499,13 +488,13 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - private void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) { + public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) { synchronized (mBoundsChangedListeners) { mBoundsChangedListeners.add(new WeakReference<>(listener)); } } - private boolean splitPrimaryTask() { + public boolean splitPrimaryTask() { try { if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED || isSplitActive()) { @@ -538,12 +527,12 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays topRunningTask.taskId, true /* onTop */); } - private void dismissSplitToPrimaryTask() { + public void dismissSplitToPrimaryTask() { startDismissSplit(true /* toPrimaryTask */); } /** Notifies the bounds of split screen changed. */ - void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) { + public void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) { synchronized (mBoundsChangedListeners) { mBoundsChangedListeners.removeIf(wf -> { BiConsumer<Rect, Rect> l = wf.get(); @@ -553,19 +542,19 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - void startEnterSplit() { + public void startEnterSplit() { update(mDisplayController.getDisplayContext( mContext.getDisplayId()).getResources().getConfiguration()); // Set resizable directly here because applyEnterSplit already resizes home stack. mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout); } - void prepareEnterSplitTransition(WindowContainerTransaction outWct) { + public void prepareEnterSplitTransition(WindowContainerTransaction outWct) { // Set resizable directly here because buildEnterSplit already resizes home stack. mHomeStackResizable = mWindowManagerProxy.buildEnterSplit(outWct, mSplits, mSplitLayout); } - void finishEnterSplitTransition(boolean minimized) { + public void finishEnterSplitTransition(boolean minimized) { update(mDisplayController.getDisplayContext( mContext.getDisplayId()).getResources().getConfiguration()); if (minimized) { @@ -575,11 +564,11 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - void startDismissSplit(boolean toPrimaryTask) { + public void startDismissSplit(boolean toPrimaryTask) { startDismissSplit(toPrimaryTask, false /* snapped */); } - void startDismissSplit(boolean toPrimaryTask, boolean snapped) { + public void startDismissSplit(boolean toPrimaryTask, boolean snapped) { if (Transitions.ENABLE_SHELL_TRANSITIONS) { mSplits.getSplitTransitions().dismissSplit( mSplits, mSplitLayout, !toPrimaryTask, snapped); @@ -589,7 +578,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - void onDismissSplit() { + public void onDismissSplit() { updateVisibility(false /* visible */); mMinimized = false; // Resets divider bar position to undefined, so new divider bar will apply default position @@ -599,7 +588,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays mImePositionProcessor.reset(); } - void ensureMinimizedSplit() { + public void ensureMinimizedSplit() { setHomeMinimized(true /* minimized */); if (mView != null && !isDividerVisible()) { // Wasn't in split-mode yet, so enter now. @@ -610,7 +599,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - void ensureNormalSplit() { + public void ensureNormalSplit() { setHomeMinimized(false /* minimized */); if (mView != null && !isDividerVisible()) { // Wasn't in split-mode, so enter now. @@ -621,15 +610,15 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays } } - LegacySplitDisplayLayout getSplitLayout() { + public LegacySplitDisplayLayout getSplitLayout() { return mSplitLayout; } - WindowManagerProxy getWmProxy() { + public WindowManagerProxy getWmProxy() { return mWindowManagerProxy; } - WindowContainerToken getSecondaryRoot() { + public WindowContainerToken getSecondaryRoot() { if (mSplits == null || mSplits.mSecondary == null) { return null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index e95864873c0c..11c11f44a781 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -69,9 +69,4 @@ public interface OneHanded { * 3 button navigation mode only */ void registerGestureCallback(OneHandedGestureEventCallback callback); - - /** - * Dump one handed status. - */ - void dump(@NonNull PrintWriter pw); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index eaa704f22410..5a3c38b09ec6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -134,10 +134,10 @@ public class OneHandedController { /** - * Creates {@link OneHanded}, returns {@code null} if the feature is not supported. + * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported. */ @Nullable - public static OneHanded create( + public static OneHandedController create( Context context, DisplayController displayController, TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) { @@ -166,7 +166,7 @@ public class OneHandedController { return new OneHandedController(context, displayController, oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler, gestureHandler, timeoutHandler, oneHandedUiEventsLogger, overlayManager, - taskStackListener, mainExecutor, mainHandler).mImpl; + taskStackListener, mainExecutor, mainHandler); } @VisibleForTesting @@ -228,6 +228,10 @@ public class OneHandedController { mAccessibilityStateChangeListener); } + public OneHanded asOneHanded() { + return mImpl; + } + /** * Set one handed enabled or disabled when user update settings */ @@ -468,7 +472,7 @@ public class OneHandedController { } } - private void dump(@NonNull PrintWriter pw) { + public void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "mOffSetFraction="); @@ -561,12 +565,5 @@ public class OneHandedController { OneHandedController.this.registerGestureCallback(callback); }); } - - @Override - public void dump(@NonNull PrintWriter pw) { - mMainExecutor.execute(() -> { - OneHandedController.this.dump(pw); - }); - } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index ad6f435ea907..3064af6f5170 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -67,6 +67,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.transition.Transitions; @@ -131,7 +132,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final PipUiEventLogger mPipUiEventLoggerLogger; private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; - private final Optional<LegacySplitScreen> mSplitScreenOptional; + private final Optional<LegacySplitScreenController> mSplitScreenOptional; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellExecutor mMainExecutor; @@ -207,7 +208,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @NonNull PipAnimationController pipAnimationController, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @NonNull PipTransitionController pipTransitionController, - Optional<LegacySplitScreen> splitScreenOptional, + Optional<LegacySplitScreenController> splitScreenOptional, @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, @NonNull ShellTaskOrganizer shellTaskOrganizer, @@ -1047,7 +1048,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * Sync with {@link LegacySplitScreen} on destination bounds if PiP is going to split screen. + * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split + * screen. * * @param destinationBoundsOut contain the updated destination bounds if applicable * @return {@code true} if destinationBounds is altered for split screen @@ -1057,7 +1059,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return false; } - LegacySplitScreen legacySplitScreen = mSplitScreenOptional.get(); + LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get(); if (!legacySplitScreen.isDividerVisible()) { // fail early if system is not in split screen mode return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java deleted file mode 100644 index 11f22ed24a69..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.sizecompatui; - -import android.annotation.Nullable; -import android.graphics.Rect; -import android.os.IBinder; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.annotations.ExternalThread; - -/** - * Interface to engage size compat mode UI. - */ -@ExternalThread -public interface SizeCompatUI { - /** - * Called when the Task info changed. Creates and updates the restart button if there is an - * activity in size compat, or removes the restart button if there is no size compat activity. - * - * @param displayId display the task and activity are in. - * @param taskId task the activity is in. - * @param taskBounds task bounds to place the restart button in. - * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the - * top activity in this Task is not in size compat. - * @param taskListener listener to handle the Task Surface placement. - */ - void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds, - @Nullable IBinder sizeCompatActivity, - @Nullable ShellTaskOrganizer.TaskListener taskListener); -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java index 286c3b6a051e..48ee86c4954f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java @@ -48,7 +48,6 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0); @VisibleForTesting - final SizeCompatUI mImpl = new SizeCompatUIImpl(); private final Context mContext; private final ShellExecutor mMainExecutor; private final DisplayController mDisplayController; @@ -57,17 +56,8 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang /** Only show once automatically in the process life. */ private boolean mHasShownHint; - /** Creates the {@link SizeCompatUIController}. */ - public static SizeCompatUI create(Context context, - DisplayController displayController, - DisplayImeController imeController, - ShellExecutor mainExecutor) { - return new SizeCompatUIController(context, displayController, imeController, mainExecutor) - .mImpl; - } - @VisibleForTesting - SizeCompatUIController(Context context, + public SizeCompatUIController(Context context, DisplayController displayController, DisplayImeController imeController, ShellExecutor mainExecutor) { @@ -79,7 +69,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang mImeController.addPositionProcessor(this); } - private void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds, + public void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds, @Nullable IBinder sizeCompatActivity, @Nullable ShellTaskOrganizer.TaskListener taskListener) { // TODO Draw button on Task surface @@ -177,15 +167,4 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } return context; } - - private class SizeCompatUIImpl implements SizeCompatUI { - @Override - public void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds, - @Nullable IBinder sizeCompatActivity, - @Nullable ShellTaskOrganizer.TaskListener taskListener) { - mMainExecutor.execute(() -> - SizeCompatUIController.this.onSizeCompatInfoChanged(displayId, taskId, - taskBounds, sizeCompatActivity, taskListener)); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 2d4b77e0c630..e44c820a656a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -156,7 +156,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } void setSideStageVisibility(boolean visible) { - if (!mSideStageListener.mVisible == visible) return; + if (mSideStageListener.mVisible == visible) return; final WindowContainerTransaction wct = new WindowContainerTransaction(); mSideStage.setVisibility(visible, wct); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 80ea9b9e177e..176b33dda020 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -51,7 +51,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.sizecompatui.SizeCompatUI; +import com.android.wm.shell.sizecompatui.SizeCompatUIController; import org.junit.Before; import org.junit.Test; @@ -76,7 +76,7 @@ public class ShellTaskOrganizerTests { @Mock private Context mContext; @Mock - private SizeCompatUI mSizeCompatUI; + private SizeCompatUIController mSizeCompatUI; ShellTaskOrganizer mOrganizer; private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 9430af946899..d10c03677d30 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -44,6 +44,7 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PhonePipMenuController; import org.junit.Before; @@ -70,7 +71,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private PipTransitionController mMockPipTransitionController; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; - @Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen; + @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; private TestShellExecutor mMainExecutor; private PipBoundsState mPipBoundsState; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java index 98f01ff08deb..0eb64e5963d1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java @@ -86,7 +86,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { final Rect taskBounds = new Rect(0, 0, 1000, 2000); // Verify that the restart button is added with non-null size compat activity. - mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds, + mController.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds, mMockActivityToken, mMockTaskListener); mShellMainExecutor.flushAll(); @@ -94,7 +94,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken)); // Verify that the restart button is removed with null size compat activity. - mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null); + mController.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null); mShellMainExecutor.flushAll(); verify(mMockButton).remove(); @@ -104,7 +104,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testChangeButtonVisibilityOnImeShowHide() { final int taskId = 12; final Rect taskBounds = new Rect(0, 0, 1000, 2000); - mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds, + mController.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds, mMockActivityToken, mMockTaskListener); mShellMainExecutor.flushAll(); diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index ca5981c0dd5c..9c743cea592a 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -83,8 +83,16 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, return {}; } + std::unique_ptr<AssetsProvider> overlay_assets; const std::string overlay_path(loaded_idmap->OverlayApkPath()); - auto overlay_assets = ZipAssetsProvider::Create(overlay_path); + if (IsFabricatedOverlay(overlay_path)) { + // Fabricated overlays do not contain resource definitions. All of the overlay resource values + // are defined inline in the idmap. + overlay_assets = EmptyAssetsProvider::Create(); + } else { + // The overlay should be an APK. + overlay_assets = ZipAssetsProvider::Create(overlay_path); + } if (overlay_assets == nullptr) { return {}; } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 03ab62f48870..36bde5ccf267 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -102,9 +102,8 @@ AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } -bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, - bool invalidate_caches) { - apk_assets_ = apk_assets; +bool AssetManager2::SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches) { + apk_assets_ = std::move(apk_assets); BuildDynamicRefTable(); RebuildFilterList(); if (invalidate_caches) { @@ -137,6 +136,36 @@ void AssetManager2::BuildDynamicRefTable() { // 0x01 is reserved for the android package. int next_package_id = 0x02; for (const ApkAssets* apk_assets : sorted_apk_assets) { + std::shared_ptr<OverlayDynamicRefTable> overlay_ref_table; + if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) { + // The target package must precede the overlay package in the apk assets paths in order + // to take effect. + auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath())); + if (iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { + uint8_t target_package_id = iter->second; + + // Create a special dynamic reference table for the overlay to rewrite references to + // overlay resources as references to the target resources they overlay. + overlay_ref_table = std::make_shared<OverlayDynamicRefTable>( + loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); + + // Add the overlay resource map to the target package's set of overlays. + const uint8_t target_idx = package_ids_[target_package_id]; + CHECK(target_idx != 0xff) << "overlay target '" << loaded_idmap->TargetApkPath() + << "'added to apk_assets_package_ids but does not have an" + << " assigned package group"; + + PackageGroup& target_package_group = package_groups_[target_idx]; + target_package_group.overlays_.push_back( + ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, + overlay_ref_table.get()), + apk_assets_cookies[apk_assets]}); + } + } + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. @@ -147,50 +176,25 @@ void AssetManager2::BuildDynamicRefTable() { package_id = package->GetPackageId(); } - // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; if (idx == 0xff) { + // Add the mapping for package ID to index if not present. package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size()); - package_groups_.push_back({}); - - if (apk_assets->IsOverlay()) { - // The target package must precede the overlay package in the apk assets paths in order - // to take effect. - const auto& loaded_idmap = apk_assets->GetLoadedIdmap(); - auto target_package_iter = apk_assets_package_ids.find( - std::string(loaded_idmap->TargetApkPath())); - if (target_package_iter == apk_assets_package_ids.end()) { - LOG(INFO) << "failed to find target package for overlay " - << loaded_idmap->OverlayApkPath(); - } else { - const uint8_t target_package_id = target_package_iter->second; - const uint8_t target_idx = package_ids_[target_package_id]; - CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not" - << " have an assigned package group"; - - PackageGroup& target_package_group = package_groups_[target_idx]; - - // Create a special dynamic reference table for the overlay to rewrite references to - // overlay resources as references to the target resources they overlay. - auto overlay_table = std::make_shared<OverlayDynamicRefTable>( - loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); - package_groups_.back().dynamic_ref_table = overlay_table; - - // Add the overlay resource map to the target package's set of overlays. - target_package_group.overlays_.push_back( - ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, - overlay_table.get()), - apk_assets_cookies[apk_assets]}); - } + PackageGroup& new_group = package_groups_.emplace_back(); + + if (overlay_ref_table != nullptr) { + // If this package is from an overlay, use a dynamic reference table that can rewrite + // overlay resource ids to their corresponding target resource ids. + new_group.dynamic_ref_table = overlay_ref_table; } - DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get(); + DynamicRefTable* ref_table = new_group.dynamic_ref_table.get(); ref_table->mAssignedPackageId = package_id; ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } - PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. + PackageGroup* package_group = &package_groups_[idx]; package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(apk_assets_cookies[apk_assets]); @@ -578,7 +582,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( const PackageGroup& package_group = package_groups_[package_idx]; auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, - stop_at_first_match, ignore_configuration); + stop_at_first_match, ignore_configuration); if (UNLIKELY(!result.has_value())) { return base::unexpected(result.error()); } diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index 23cacf88a6db..f3c48f7f9fc8 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -84,7 +84,7 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const { return value_; } -ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, +ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path, time_t last_mod_time) : zip_handle_(handle, ::CloseArchive), name_(std::forward<PathOrDebugName>(path)), @@ -93,7 +93,7 @@ ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) { ZipArchiveHandle handle; if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result); CloseArchive(handle); return {}; } @@ -253,6 +253,14 @@ bool ZipAssetsProvider::ForEachFile(const std::string& root_path, return result == -1; } +std::optional<uint32_t> ZipAssetsProvider::GetCrc(std::string_view path) const { + ::ZipEntry entry; + if (FindEntry(zip_handle_.get(), path, &entry) != 0) { + return {}; + } + return entry.crc32; +} + const std::string& ZipAssetsProvider::GetDebugName() const { return name_.GetDebugName(); } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index f216f55771c2..efd1f6a25786 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -54,12 +54,6 @@ struct Idmap_header { }; struct Idmap_data_header { - uint8_t target_package_id; - uint8_t overlay_package_id; - - // Padding to ensure 4 byte alignment for target_entry_count - uint16_t p0; - uint32_t target_entry_count; uint32_t target_inline_entry_count; uint32_t overlay_entry_count; @@ -158,19 +152,19 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { return {}; } - // The resource ids encoded within the idmap are build-time resource ids. - target_res_id = (0x00FFFFFFU & target_res_id) - | (((uint32_t) data_header_->target_package_id) << 24U); + // The resource ids encoded within the idmap are build-time resource ids so do not consider the + // package id when determining if the resource in the target package is overlaid. + target_res_id &= 0x00FFFFFFU; // Check if the target resource is mapped to an overlay resource. auto first_entry = entries_; auto end_entry = entries_ + dtohl(data_header_->target_entry_count); auto entry = std::lower_bound(first_entry, end_entry, target_res_id, - [](const Idmap_target_entry &e, const uint32_t target_id) { - return dtohl(e.target_id) < target_id; + [](const Idmap_target_entry& e, const uint32_t target_id) { + return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; }); - if (entry != end_entry && dtohl(entry->target_id) == target_res_id) { + if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) { uint32_t overlay_resource_id = dtohl(entry->overlay_id); // Lookup the resource without rewriting the overlay resource id back to the target resource id // being looked up. @@ -182,12 +176,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { auto first_inline_entry = inline_entries_; auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count); auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id, - [](const Idmap_target_entry_inline &e, + [](const Idmap_target_entry_inline& e, const uint32_t target_id) { - return dtohl(e.target_id) < target_id; + return (0x00FFFFFFU & dtohl(e.target_id)) < target_id; }); - if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) { + if (inline_entry != end_inline_entry && + (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) { return Result(inline_entry->value); } return {}; @@ -235,7 +230,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size } return std::string_view(data, *len); } -} +} // namespace LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header, diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 223382731bc0..30500abc39c0 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -25,6 +25,7 @@ #include <string.h> #include <algorithm> +#include <fstream> #include <limits> #include <map> #include <memory> @@ -44,6 +45,7 @@ #ifdef __ANDROID__ #include <binder/TextOutput.h> + #endif #ifndef INT32_MAX @@ -233,6 +235,15 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData)); } +bool IsFabricatedOverlay(const std::string& path) { + std::ifstream fin(path); + uint32_t magic; + if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) { + return magic == kFabricatedOverlayMagic; + } + return false; +} + static bool assertIdmapHeader(const void* idmap, size_t size) { if (reinterpret_cast<uintptr_t>(idmap) & 0x03) { ALOGE("idmap: header is not word aligned"); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 6fbd6aa0df7b..2255973f1039 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -101,7 +101,7 @@ class AssetManager2 { // Only pass invalidate_caches=false when it is known that the structure // change in ApkAssets is due to a safe addition of resources with completely // new resource IDs. - bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true); + bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true); inline const std::vector<const ApkAssets*> GetApkAssets() const { return apk_assets_; diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 7b06947f45aa..6f16ff453905 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -88,6 +88,8 @@ struct ZipAssetsProvider : public AssetsProvider { WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; + WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const; + ~ZipAssetsProvider() override = default; protected: std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode, diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index 0ded79309bc1..6804472b3d17 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -168,15 +168,14 @@ class LoadedIdmap { } // Returns a mapping from target resource ids to overlay values. - const IdmapResMap GetTargetResourcesMap( - uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { + const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table) const { return IdmapResMap(data_header_, target_entries_, target_inline_entries_, target_assigned_package_id, overlay_ref_table); } // Returns a dynamic reference table for a loaded overlay package. - const OverlayDynamicRefTable GetOverlayDynamicRefTable( - uint8_t target_assigned_package_id) const { + const OverlayDynamicRefTable GetOverlayDynamicRefTable(uint8_t target_assigned_package_id) const { return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id); } diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index bfd564c258ee..168a863df2bc 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -43,8 +43,19 @@ namespace android { -constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000007u; +constexpr const uint32_t kIdmapMagic = 0x504D4449u; +constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u; + +// This must never change. +constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian) + +// 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; + +// Returns whether or not the path represents a fabricated overlay. +bool IsFabricatedOverlay(const std::string& path); /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap Binary files differindex 723413c3cea8..88eadccb38cf 100644 --- a/libs/androidfw/tests/data/overlay/overlay.idmap +++ b/libs/androidfw/tests/data/overlay/overlay.idmap diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index 8f455fe4ab43..18423562bc56 100644 --- a/libs/hwui/jni/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -355,29 +355,41 @@ static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring f env->SetStaticObjectField(cls, fid, typeface); } +// Critical Native +static jint Typeface_getFamilySize(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { + return toTypeface(faceHandle)->fFontCollection->getFamilies().size(); +} + +// Critical Native +static jlong Typeface_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle, jint index) { + std::shared_ptr<minikin::FontFamily> family = + toTypeface(faceHandle)->fFontCollection->getFamilies()[index]; + return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family))); +} /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gTypefaceMethods[] = { - { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface }, - { "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J", - (void*)Typeface_createFromTypefaceWithExactStyle }, - { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J", - (void*)Typeface_createFromTypefaceWithVariation }, - { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias }, - { "nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc }, - { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle }, - { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight }, - { "nativeCreateFromArray", "([JJII)J", - (void*)Typeface_createFromArray }, - { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault }, - { "nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes }, - { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V", - (void*)Typeface_registerGenericFamily }, - { "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces}, - { "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces}, - { "nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V", - (void*)Typeface_forceSetStaticFinalField }, + {"nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface}, + {"nativeCreateFromTypefaceWithExactStyle", "(JIZ)J", + (void*)Typeface_createFromTypefaceWithExactStyle}, + {"nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J", + (void*)Typeface_createFromTypefaceWithVariation}, + {"nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias}, + {"nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc}, + {"nativeGetStyle", "(J)I", (void*)Typeface_getStyle}, + {"nativeGetWeight", "(J)I", (void*)Typeface_getWeight}, + {"nativeCreateFromArray", "([JJII)J", (void*)Typeface_createFromArray}, + {"nativeSetDefault", "(J)V", (void*)Typeface_setDefault}, + {"nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes}, + {"nativeRegisterGenericFamily", "(Ljava/lang/String;J)V", + (void*)Typeface_registerGenericFamily}, + {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces}, + {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces}, + {"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V", + (void*)Typeface_forceSetStaticFinalField}, + {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize}, + {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily}, }; int register_android_graphics_Typeface(JNIEnv* env) diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index d291ec001daf..438a92ef8fb5 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -95,7 +95,7 @@ cc_test { name: "libincident_test", test_config: "AndroidTest.xml", defaults: ["libincidentpriv_defaults"], - test_suites: ["device-tests", "mts"], + test_suites: ["device-tests", "mts-statsd"], compile_multilib: "both", multilib: { lib64: { diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl index 02fa94c845a6..5a7ff7fbd1ae 100644 --- a/media/java/android/media/IRingtonePlayer.aidl +++ b/media/java/android/media/IRingtonePlayer.aidl @@ -33,7 +33,8 @@ interface IRingtonePlayer { float volume, boolean looping, in @nullable VolumeShaper.Configuration volumeShaperConfig); oneway void stop(IBinder token); boolean isPlaying(IBinder token); - oneway void setPlaybackProperties(IBinder token, float volume, boolean looping); + oneway void setPlaybackProperties(IBinder token, float volume, boolean looping, + boolean hapticGeneratorEnabled); /** Used for Notification sound playback. */ oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa); diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index bd783ce9f6b2..79d505ebbde8 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.Resources.NotFoundException; import android.database.Cursor; +import android.media.audiofx.HapticGenerator; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -77,6 +78,7 @@ public class Ringtone { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private MediaPlayer mLocalPlayer; private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener(); + private HapticGenerator mHapticGenerator; @UnsupportedAppUsage private Uri mUri; @@ -89,6 +91,7 @@ public class Ringtone { // playback properties, use synchronized with mPlaybackSettingsLock private boolean mIsLooping = false; private float mVolume = 1.0f; + private boolean mHapticGeneratorEnabled = false; private final Object mPlaybackSettingsLock = new Object(); /** {@hide} */ @@ -197,15 +200,50 @@ public class Ringtone { } /** + * Enable or disable the {@link android.media.audiofx.HapticGenerator} effect. The effect can + * only be enabled on devices that support the effect. + * + * @return true if the HapticGenerator effect is successfully enabled. Otherwise, return false. + * @see android.media.audiofx.HapticGenerator#isAvailable() + */ + public boolean setHapticGeneratorEnabled(boolean enabled) { + if (!HapticGenerator.isAvailable()) { + return false; + } + synchronized (mPlaybackSettingsLock) { + mHapticGeneratorEnabled = enabled; + applyPlaybackProperties_sync(); + } + return true; + } + + /** + * Return whether the {@link android.media.audiofx.HapticGenerator} effect is enabled or not. + * @return true if the HapticGenerator is enabled. + */ + public boolean isHapticGeneratorEnabled() { + synchronized (mPlaybackSettingsLock) { + return mHapticGeneratorEnabled; + } + } + + /** * Must be called synchronized on mPlaybackSettingsLock */ private void applyPlaybackProperties_sync() { if (mLocalPlayer != null) { mLocalPlayer.setVolume(mVolume); mLocalPlayer.setLooping(mIsLooping); + if (mHapticGenerator == null && mHapticGeneratorEnabled) { + mHapticGenerator = HapticGenerator.create(mLocalPlayer.getAudioSessionId()); + } + if (mHapticGenerator != null) { + mHapticGenerator.setEnabled(mHapticGeneratorEnabled); + } } else if (mAllowRemote && (mRemotePlayer != null)) { try { - mRemotePlayer.setPlaybackProperties(mRemoteToken, mVolume, mIsLooping); + mRemotePlayer.setPlaybackProperties( + mRemoteToken, mVolume, mIsLooping, mHapticGeneratorEnabled); } catch (RemoteException e) { Log.w(TAG, "Problem setting playback properties: ", e); } @@ -413,6 +451,10 @@ public class Ringtone { private void destroyLocalPlayer() { if (mLocalPlayer != null) { + if (mHapticGenerator != null) { + mHapticGenerator.release(); + mHapticGenerator = null; + } mLocalPlayer.setOnCompletionListener(null); mLocalPlayer.reset(); mLocalPlayer.release(); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 98b9ad8ec2b3..740bc2dbeb43 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -2526,6 +2526,7 @@ public final class TvInputManager { * Pauses TV program recording in the current recording session. * * @param params A set of extra parameters which might be handled with this event. + * {@link TvRecordingClient#pauseRecording(Bundle)}. */ void pauseRecording(@NonNull Bundle params) { if (mToken == null) { @@ -2543,6 +2544,7 @@ public final class TvInputManager { * Resumes TV program recording in the current recording session. * * @param params A set of extra parameters which might be handled with this event. + * {@link TvRecordingClient#resumeRecording(Bundle)}. */ void resumeRecording(@NonNull Bundle params) { if (mToken == null) { diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 65b64d7e8df3..4972529eb705 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -180,6 +180,10 @@ cc_library_shared { "libstagefright_foundation_headers", ], + // TunerService is a system service required for Tuner feature. + // TunerJNI is a client of TunerService so we build the dependency here. + required: ["mediatuner"], + export_include_dirs: ["."], cflags: [ diff --git a/media/jni/tuner/ClientHelper.h b/media/jni/tuner/ClientHelper.h index 185b2f651d29..508dccf36841 100644 --- a/media/jni/tuner/ClientHelper.h +++ b/media/jni/tuner/ClientHelper.h @@ -19,6 +19,7 @@ #include <android/binder_parcel_utils.h> #include <android/hardware/tv/tuner/1.1/types.h> +#include <utils/Log.h> using Status = ::ndk::ScopedAStatus; @@ -37,6 +38,7 @@ public: } else if (s.isOk()) { return Result::SUCCESS; } + ALOGE("Aidl exception code %s", s.getDescription().c_str()); return Result::UNKNOWN_ERROR; } }; diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp index 8b4ca371056e..f61889035432 100644 --- a/media/jni/tuner/FilterClient.cpp +++ b/media/jni/tuner/FilterClient.cpp @@ -43,6 +43,7 @@ using ::android::hardware::tv::tuner::V1_0::DemuxStreamId; using ::android::hardware::tv::tuner::V1_0::DemuxTpid; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType; +using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent; using ::android::hardware::tv::tuner::V1_1::ScramblingStatus; namespace android { @@ -480,7 +481,7 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr, case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v4: { int size = ipAddr.srcIpAddress.v4().size(); srcIpAddress.isIpV6 = false; - srcIpAddress.addr.resize(ipAddr.srcIpAddress.v4().size()); + srcIpAddress.addr.resize(size); copy(&ipAddr.srcIpAddress.v4()[0], &ipAddr.srcIpAddress.v4()[size], srcIpAddress.addr.begin()); break; @@ -493,8 +494,6 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr, srcIpAddress.addr.begin()); break; } - default: - break; } switch (ipAddr.dstIpAddress.getDiscriminator()) { case DemuxIpAddress::DstIpAddress::hidl_discriminator::v4: { @@ -513,8 +512,6 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr, dstIpAddress.addr.begin()); break; } - default: - break; } } @@ -696,8 +693,6 @@ void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& fil getHidlRestartEvent(filterEvents, eventExt); break; } - default: - break; } } @@ -883,19 +878,18 @@ void TunerFilterCallback::getHidlMonitorEvent(const vector<TunerFilterEvent>& fi DemuxFilterEventExt& eventExt) { auto monitor = filterEvents[0].get<TunerFilterEvent::monitor>(); eventExt.events.resize(1); + DemuxFilterMonitorEvent monitorEvent; switch (monitor.getTag()) { case TunerFilterMonitorEvent::scramblingStatus: { - eventExt.events[0].monitorEvent().scramblingStatus( - static_cast<ScramblingStatus>(monitor.scramblingStatus)); + monitorEvent.scramblingStatus(static_cast<ScramblingStatus>(monitor.scramblingStatus)); + eventExt.events[0].monitorEvent(monitorEvent); break; } case TunerFilterMonitorEvent::cid: { - eventExt.events[0].monitorEvent().cid(static_cast<uint32_t>(monitor.cid)); + monitorEvent.cid(static_cast<uint32_t>(monitor.cid)); + eventExt.events[0].monitorEvent(monitorEvent); break; } - default: - eventExt.events[0].noinit(); - break; } } diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp index 3a00133c69e2..0613223bd5dc 100644 --- a/media/jni/tuner/FrontendClient.cpp +++ b/media/jni/tuner/FrontendClient.cpp @@ -49,9 +49,12 @@ using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation; +using ::android::hardware::tv::tuner::V1_0::FrontendModulationStatus; using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo; +using ::android::hardware::tv::tuner::V1_0::FrontendStatusAtsc3PlpInfo; using ::android::hardware::tv::tuner::V1_0::LnbVoltage; using ::android::hardware::tv::tuner::V1_1::Constant; +using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth; using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode; using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth; using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval; @@ -61,19 +64,22 @@ using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode; using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth; using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation; using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode; +using ::android::hardware::tv::tuner::V1_1::FrontendGuardInterval; +using ::android::hardware::tv::tuner::V1_1::FrontendInterleaveMode; using ::android::hardware::tv::tuner::V1_1::FrontendModulation; +using ::android::hardware::tv::tuner::V1_1::FrontendRollOff; using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion; +using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode; using ::android::hardware::tv::tuner::V1_1::FrontendType; namespace android { /////////////// FrontendClient /////////////////////// -FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type) { +FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type) { mTunerFrontend = tunerFrontend; mAidlCallback = NULL; mHidlCallback = NULL; - mId = id; mType = type; } @@ -104,6 +110,11 @@ void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) { mFrontend_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFrontend); } +// TODO: move after migration is done +void FrontendClient::setId(int id) { + mId = id; +} + Result FrontendClient::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) { if (mTunerFrontend != NULL) { @@ -333,13 +344,26 @@ shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() { } int FrontendClient::getId() { - return mId; + if (mTunerFrontend != NULL) { + Status s = mTunerFrontend->getFrontendId(&mId); + if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) { + return mId; + } + ALOGE("Failed to getFrontendId from Tuner Frontend"); + return -1; + } + + if (mFrontend != NULL) { + return mId; + } + + return -1; } vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) { vector<FrontendStatus> hidlStatus; for (TunerFrontendStatus s : aidlStatus) { - FrontendStatus status; + FrontendStatus status = FrontendStatus(); switch (s.getTag()) { case TunerFrontendStatus::isDemodLocked: { status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>()); @@ -389,25 +413,31 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus> } case TunerFrontendStatus::modulation: { auto aidlMod = s.get<TunerFrontendStatus::modulation>(); + FrontendModulationStatus modulation; switch (mType) { case (int)FrontendType::DVBC: - status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod)); + modulation.dvbc(static_cast<FrontendDvbcModulation>(aidlMod)); + status.modulation(modulation); hidlStatus.push_back(status); break; case (int)FrontendType::DVBS: - status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod)); + modulation.dvbs(static_cast<FrontendDvbsModulation>(aidlMod)); + status.modulation(modulation); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBS: - status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod)); + modulation.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod)); + status.modulation(modulation); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBS3: - status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod)); + modulation.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod)); + status.modulation(modulation); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBT: - status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod)); + modulation.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod)); + status.modulation(modulation); hidlStatus.push_back(status); break; default: @@ -466,7 +496,7 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus> } case TunerFrontendStatus::hierarchy: { status.hierarchy(static_cast<FrontendDvbtHierarchy>( - s.get<TunerFrontendStatus::freqOffset>())); + s.get<TunerFrontendStatus::hierarchy>())); hidlStatus.push_back(status); break; } @@ -477,15 +507,16 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus> } case TunerFrontendStatus::plpInfo: { int size = s.get<TunerFrontendStatus::plpInfo>().size(); - status.plpInfo().resize(size); + hidl_vec<FrontendStatusAtsc3PlpInfo> info(size); for (int i = 0; i < size; i++) { auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i]; - status.plpInfo()[i] = { + info[i] = { .plpId = (uint8_t)aidlInfo.plpId, .isLocked = aidlInfo.isLocked, .uec = (uint32_t)aidlInfo.uec, }; } + status.plpInfo(info); hidlStatus.push_back(status); break; } @@ -503,52 +534,54 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( FrontendStatusExt1_1 status; switch (s.getTag()) { case TunerFrontendStatus::modulations: { + vector<FrontendModulation> ms; for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) { - int size = status.modulations().size(); - status.modulations().resize(size + 1); + FrontendModulation m; switch (mType) { case (int)FrontendType::DVBC: - status.modulations()[size].dvbc( - static_cast<FrontendDvbcModulation>(aidlMod)); + m.dvbc(static_cast<FrontendDvbcModulation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::DVBS: - status.modulations()[size].dvbs( - static_cast<FrontendDvbsModulation>(aidlMod)); + m.dvbs(static_cast<FrontendDvbsModulation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::DVBT: - status.modulations()[size].dvbt( - static_cast<FrontendDvbtConstellation>(aidlMod)); + m.dvbt(static_cast<FrontendDvbtConstellation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::ISDBS: - status.modulations()[size].isdbs( - static_cast<FrontendIsdbsModulation>(aidlMod)); + m.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::ISDBS3: - status.modulations()[size].isdbs3( - static_cast<FrontendIsdbs3Modulation>(aidlMod)); + m.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::ISDBT: - status.modulations()[size].isdbt( - static_cast<FrontendIsdbtModulation>(aidlMod)); + m.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::ATSC: - status.modulations()[size].atsc( - static_cast<FrontendAtscModulation>(aidlMod)); + m.atsc(static_cast<FrontendAtscModulation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::ATSC3: - status.modulations()[size].atsc3( - static_cast<FrontendAtsc3Modulation>(aidlMod)); + m.atsc3(static_cast<FrontendAtsc3Modulation>(aidlMod)); + ms.push_back(m); break; case (int)FrontendType::DTMB: - status.modulations()[size].dtmb( - static_cast<FrontendDtmbModulation>(aidlMod)); + m.dtmb(static_cast<FrontendDtmbModulation>(aidlMod)); + ms.push_back(m); break; default: - status.modulations().resize(size); break; } } - hidlStatus.push_back(status); + if (ms.size() > 0) { + status.modulations(ms); + hidlStatus.push_back(status); + } break; } case TunerFrontendStatus::bers: { @@ -571,25 +604,31 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( } case TunerFrontendStatus::bandwidth: { auto aidlBand = s.get<TunerFrontendStatus::bandwidth>(); + FrontendBandwidth band; switch (mType) { case (int)FrontendType::ATSC3: - status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand)); + band.atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand)); + status.bandwidth(band); hidlStatus.push_back(status); break; case (int)FrontendType::DVBC: - status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand)); + band.dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand)); + status.bandwidth(band); hidlStatus.push_back(status); break; case (int)FrontendType::DVBT: - status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand)); + band.dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand)); + status.bandwidth(band); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBT: - status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand)); + band.isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand)); + status.bandwidth(band); hidlStatus.push_back(status); break; case (int)FrontendType::DTMB: - status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand)); + band.dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand)); + status.bandwidth(band); hidlStatus.push_back(status); break; default: @@ -599,17 +638,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( } case TunerFrontendStatus::interval: { auto aidlInter = s.get<TunerFrontendStatus::interval>(); + FrontendGuardInterval inter; switch (mType) { case (int)FrontendType::DVBT: - status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter)); + inter.dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter)); + status.interval(inter); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBT: - status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter)); + inter.isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter)); + status.interval(inter); hidlStatus.push_back(status); break; case (int)FrontendType::DTMB: - status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter)); + inter.dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter)); + status.interval(inter); hidlStatus.push_back(status); break; default: @@ -619,19 +662,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( } case TunerFrontendStatus::transmissionMode: { auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>(); + FrontendTransmissionMode trans; switch (mType) { case (int)FrontendType::DVBT: - status.transmissionMode().dvbt( - static_cast<FrontendDvbtTransmissionMode>(aidlTran)); + trans.dvbt(static_cast<FrontendDvbtTransmissionMode>(aidlTran)); + status.transmissionMode(trans); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBT: - status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran)); + trans.isdbt(static_cast<FrontendIsdbtMode>(aidlTran)); + status.transmissionMode(trans); hidlStatus.push_back(status); break; case (int)FrontendType::DTMB: - status.transmissionMode().dtmb( - static_cast<FrontendDtmbTransmissionMode>(aidlTran)); + trans.dtmb(static_cast<FrontendDtmbTransmissionMode>(aidlTran)); + status.transmissionMode(trans); hidlStatus.push_back(status); break; default: @@ -650,28 +695,30 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( break; } case TunerFrontendStatus::interleaving: { + vector<FrontendInterleaveMode> modes; for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) { - int size = status.interleaving().size(); - status.interleaving().resize(size + 1); + FrontendInterleaveMode mode; switch (mType) { case (int)FrontendType::DVBC: - status.interleaving()[size].dvbc( - static_cast<FrontendCableTimeInterleaveMode>(aidlInter)); + mode.dvbc(static_cast<FrontendCableTimeInterleaveMode>(aidlInter)); + modes.push_back(mode); break; case (int)FrontendType::ATSC3: - status.interleaving()[size].atsc3( - static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter)); + mode.atsc3(static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter)); + modes.push_back(mode); break; case (int)FrontendType::DTMB: - status.interleaving()[size].dtmb( - static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter)); + mode.dtmb(static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter)); + modes.push_back(mode); break; default: - status.interleaving().resize(size); break; } } - hidlStatus.push_back(status); + if (modes.size() > 0) { + status.interleaving(modes); + hidlStatus.push_back(status); + } break; } case TunerFrontendStatus::isdbtSegment: { @@ -690,17 +737,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( } case TunerFrontendStatus::rollOff: { auto aidlRoll = s.get<TunerFrontendStatus::rollOff>(); + FrontendRollOff roll; switch (mType) { case (int)FrontendType::DVBS: - status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll)); + roll.dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll)); + status.rollOff(roll); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBS: - status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll)); + roll.isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll)); + status.rollOff(roll); hidlStatus.push_back(status); break; case (int)FrontendType::ISDBS3: - status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll)); + roll.isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll)); + status.rollOff(roll); hidlStatus.push_back(status); break; default: diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h index 298b3974aeb9..f71616cb32b7 100644 --- a/media/jni/tuner/FrontendClient.h +++ b/media/jni/tuner/FrontendClient.h @@ -108,7 +108,7 @@ private: struct FrontendClient : public RefBase { public: - FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type); + FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type); ~FrontendClient(); /** @@ -180,6 +180,7 @@ public: shared_ptr<ITunerFrontend> getAidlFrontend(); + void setId(int id); int getId(); private: diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index 7f954b561567..cf17ed6f4383 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -46,13 +46,12 @@ TunerClient::TunerClient() { // Connect with Tuner Service. ::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner")); mTunerService = ITunerService::fromBinder(binder); - // TODO: Remove after JNI migration is done. - mTunerService = NULL; if (mTunerService == NULL) { ALOGE("Failed to get tuner service"); } else { // TODO: b/178124017 update TRM in TunerService independently. mTunerService->updateTunerResources(); + mTunerService->getTunerHalVersion(&mTunerVersion); } } @@ -115,7 +114,7 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) { if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { return NULL; } - return new FrontendClient(tunerFrontend, frontendHandle, aidlFrontendInfo.type); + return new FrontendClient(tunerFrontend, aidlFrontendInfo.type); } if (mTuner != NULL) { @@ -127,8 +126,10 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) { if (res != Result::SUCCESS) { return NULL; } - sp<FrontendClient> frontendClient = new FrontendClient(NULL, id, (int)hidlInfo.type); + sp<FrontendClient> frontendClient = new FrontendClient( + NULL, (int)hidlInfo.type); frontendClient->setHidlFrontend(hidlFrontend); + frontendClient->setId(id); return frontendClient; } } @@ -358,7 +359,7 @@ void TunerClient::updateLnbResources() { sp<ITuner> TunerClient::getHidlTuner() { if (mTuner == NULL) { - mTunerVersion = 0; + mTunerVersion = TUNER_HAL_VERSION_UNKNOWN; mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService(); if (mTuner_1_1 == NULL) { @@ -367,11 +368,11 @@ sp<ITuner> TunerClient::getHidlTuner() { if (mTuner == NULL) { ALOGW("Failed to get tuner 1.0 service."); } else { - mTunerVersion = 1 << 16; + mTunerVersion = TUNER_HAL_VERSION_1_0; } } else { mTuner = static_cast<sp<ITuner>>(mTuner_1_1); - mTunerVersion = ((1 << 16) | 1); + mTunerVersion = TUNER_HAL_VERSION_1_1; } } return mTuner; diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h index 744bf2058766..9671cf787e3e 100644 --- a/media/jni/tuner/TunerClient.h +++ b/media/jni/tuner/TunerClient.h @@ -48,6 +48,10 @@ using namespace std; namespace android { +const static int TUNER_HAL_VERSION_UNKNOWN = 0; +const static int TUNER_HAL_VERSION_1_0 = 1 << 16; +const static int TUNER_HAL_VERSION_1_1 = (1 << 16) | 1; + typedef enum { FRONTEND, LNB, diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java index 9b56b23cc879..f4b46e9f11ed 100644 --- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java +++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java @@ -16,12 +16,15 @@ package android.net; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -40,10 +43,29 @@ public final class CaptivePortalData implements Parcelable { private final long mExpiryTimeMillis; private final boolean mCaptive; private final String mVenueFriendlyName; + private final int mVenueInfoUrlSource; + private final int mTermsAndConditionsSource; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CAPTIVE_PORTAL_DATA_SOURCE_"}, value = { + CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT}) + public @interface CaptivePortalDataSource {} + + /** + * Source of information: Other (default) + */ + public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; + + /** + * Source of information: Wi-Fi Passpoint + */ + public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl, boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive, - String venueFriendlyName) { + String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) { mRefreshTimeMillis = refreshTimeMillis; mUserPortalUrl = userPortalUrl; mVenueInfoUrl = venueInfoUrl; @@ -52,11 +74,14 @@ public final class CaptivePortalData implements Parcelable { mExpiryTimeMillis = expiryTimeMillis; mCaptive = captive; mVenueFriendlyName = venueFriendlyName; + mVenueInfoUrlSource = venueInfoUrlSource; + mTermsAndConditionsSource = termsAndConditionsSource; } private CaptivePortalData(Parcel p) { this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(), - p.readLong(), p.readLong(), p.readBoolean(), p.readString()); + p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(), + p.readInt()); } @Override @@ -74,6 +99,8 @@ public final class CaptivePortalData implements Parcelable { dest.writeLong(mExpiryTimeMillis); dest.writeBoolean(mCaptive); dest.writeString(mVenueFriendlyName); + dest.writeInt(mVenueInfoUrlSource); + dest.writeInt(mTermsAndConditionsSource); } /** @@ -88,6 +115,9 @@ public final class CaptivePortalData implements Parcelable { private long mExpiryTime = -1; private boolean mCaptive; private String mVenueFriendlyName; + private @CaptivePortalDataSource int mVenueInfoUrlSource = CAPTIVE_PORTAL_DATA_SOURCE_OTHER; + private @CaptivePortalDataSource int mUserPortalUrlSource = + CAPTIVE_PORTAL_DATA_SOURCE_OTHER; /** * Create an empty builder. @@ -100,8 +130,8 @@ public final class CaptivePortalData implements Parcelable { public Builder(@Nullable CaptivePortalData data) { if (data == null) return; setRefreshTime(data.mRefreshTimeMillis) - .setUserPortalUrl(data.mUserPortalUrl) - .setVenueInfoUrl(data.mVenueInfoUrl) + .setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource) + .setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource) .setSessionExtendable(data.mIsSessionExtendable) .setBytesRemaining(data.mByteLimit) .setExpiryTime(data.mExpiryTimeMillis) @@ -123,7 +153,18 @@ public final class CaptivePortalData implements Parcelable { */ @NonNull public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) { + return setUserPortalUrl(userPortalUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER); + } + + /** + * Set the URL to be used for users to login to the portal, if captive, and the source of + * the data, see {@link CaptivePortalDataSource} + */ + @NonNull + public Builder setUserPortalUrl(@Nullable Uri userPortalUrl, + @CaptivePortalDataSource int source) { mUserPortalUrl = userPortalUrl; + mUserPortalUrlSource = source; return this; } @@ -132,7 +173,18 @@ public final class CaptivePortalData implements Parcelable { */ @NonNull public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) { + return setVenueInfoUrl(venueInfoUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER); + } + + /** + * Set the URL that can be used by users to view information about the network venue, and + * the source of the data, see {@link CaptivePortalDataSource} + */ + @NonNull + public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl, + @CaptivePortalDataSource int source) { mVenueInfoUrl = venueInfoUrl; + mVenueInfoUrlSource = source; return this; } @@ -188,7 +240,8 @@ public final class CaptivePortalData implements Parcelable { public CaptivePortalData build() { return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl, mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive, - mVenueFriendlyName); + mVenueFriendlyName, mVenueInfoUrlSource, + mUserPortalUrlSource); } } @@ -249,6 +302,22 @@ public final class CaptivePortalData implements Parcelable { } /** + * Get the information source of the Venue URL + * @return The source that the Venue URL was obtained from + */ + public @CaptivePortalDataSource int getVenueInfoUrlSource() { + return mVenueInfoUrlSource; + } + + /** + * Get the information source of the user portal URL + * @return The source that the user portal URL was obtained from + */ + public @CaptivePortalDataSource int getUserPortalUrlSource() { + return mTermsAndConditionsSource; + } + + /** * Get the venue friendly name */ @Nullable @@ -272,7 +341,8 @@ public final class CaptivePortalData implements Parcelable { @Override public int hashCode() { return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, - mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName); + mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName, + mVenueInfoUrlSource, mTermsAndConditionsSource); } @Override @@ -286,7 +356,9 @@ public final class CaptivePortalData implements Parcelable { && mByteLimit == other.mByteLimit && mExpiryTimeMillis == other.mExpiryTimeMillis && mCaptive == other.mCaptive - && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName); + && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName) + && mVenueInfoUrlSource == other.mVenueInfoUrlSource + && mTermsAndConditionsSource == other.mTermsAndConditionsSource; } @Override @@ -300,6 +372,8 @@ public final class CaptivePortalData implements Parcelable { + ", expiryTime: " + mExpiryTimeMillis + ", captive: " + mCaptive + ", venueFriendlyName: " + mVenueFriendlyName + + ", venueInfoUrlSource: " + mVenueInfoUrlSource + + ", termsAndConditionsSource: " + mTermsAndConditionsSource + "}"; } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index eab0990bb868..1566b761d23e 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -87,6 +87,7 @@ <!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient --> <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" /> <uses-permission android:name="android.permission.MOVE_PACKAGE" /> + <uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" /> @@ -399,6 +400,7 @@ <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> + <uses-permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE" /> <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java index 0831e0ef7795..da079cf04403 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java @@ -103,5 +103,10 @@ public interface ToastPlugin extends Plugin { default Animator getOutAnimation() { return null; } + + /** + * Called on orientation changes. + */ + default void onOrientationChange(int orientation) { } } } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 7a9e7c7e73f6..f3ec39d2b1fb 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -74,7 +74,6 @@ android:layout_gravity="center_horizontal" android:gravity="center_horizontal" android:textSize="100dp" - android:includeFontPadding="false" android:fontFamily="@font/clock" android:typeface="monospace" android:elegantTextHeight="false" @@ -95,7 +94,6 @@ android:layout_gravity="center_horizontal" android:gravity="center_horizontal" android:textSize="@dimen/large_clock_text_size" - android:includeFontPadding="false" android:fontFamily="@font/clock" android:typeface="monospace" android:elegantTextHeight="false" diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml index 827cf4a9d3b6..109442d7ed79 100644 --- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml +++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml @@ -16,8 +16,6 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="#242424" /> <!-- 14% of white --> - <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding" - android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" /> + <solid android:color="#FFFFFF" /> <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/toast_background.xml b/packages/SystemUI/res/drawable/toast_background.xml new file mode 100644 index 000000000000..5c45e8346e3c --- /dev/null +++ b/packages/SystemUI/res/drawable/toast_background.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="#FFFFFFFF" /> + <corners android:radius="@dimen/toast_bg_radius" /> +</shape> diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml index 3c306322d21f..bad582669079 100644 --- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml @@ -22,19 +22,14 @@ android:layout_height="match_parent" android:layout_width="wrap_content" android:layout_gravity="center_vertical|end" - android:focusable="true" > + android:focusable="true" + android:minWidth="48dp" > - <FrameLayout - android:id="@+id/background" + <LinearLayout + android:id="@+id/icons_container" android:layout_height="@dimen/ongoing_appops_chip_height" android:layout_width="wrap_content" - android:minWidth="48dp" - android:layout_gravity="center_vertical"> - <LinearLayout - android:id="@+id/icons_container" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:gravity="center_vertical" - /> - </FrameLayout> + android:gravity="center_vertical" + android:layout_gravity="center" + /> </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/text_toast.xml b/packages/SystemUI/res/layout/text_toast.xml new file mode 100644 index 000000000000..de4e062805fe --- /dev/null +++ b/packages/SystemUI/res/layout/text_toast.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxWidth="@dimen/toast_width" + android:orientation="horizontal" + android:background="@drawable/toast_background" + android:backgroundTint="?android:attr/colorBackground" + android:layout_marginEnd="16dp" + android:layout_marginStart="16dp" + android:gravity="center_vertical"> + + <!-- Icon should be 24x24, make slightly larger to allow for shadowing, adjust via padding --> + <ImageView + android:id="@+id/icon" + android:alpha="@dimen/toast_icon_alpha" + android:padding="11.5dp" + android:layout_width="@dimen/toast_icon_size" + android:layout_height="@dimen/toast_icon_size"/> + <TextView + android:id="@+id/text" + android:ellipsize="end" + android:maxLines="2" + android:paddingTop="12dp" + android:paddingBottom="12dp" + android:paddingStart="0dp" + android:paddingEnd="22dp" + android:textSize="@dimen/toast_text_size" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> +</LinearLayout> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 51d7b8eff5fc..24c7655e5ae4 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -52,4 +52,6 @@ <!-- (footer_height -48dp)/2 --> <dimen name="controls_management_footer_top_margin">4dp</dimen> <dimen name="controls_management_favorites_top_margin">8dp</dimen> + + <dimen name="toast_y_offset">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 12b8ccfd09d6..08cd6553e252 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1179,17 +1179,13 @@ <dimen name="display_cutout_margin_consumption">0px</dimen> <!-- Height of the Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_height">32dp</dimen> - <!-- Padding between background of Ongoing App Ops chip and content --> - <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen> + <dimen name="ongoing_appops_chip_height">24dp</dimen> <!-- Side padding between background of Ongoing App Ops chip and content --> <dimen name="ongoing_appops_chip_side_padding">8dp</dimen> - <!-- Margin between icons of Ongoing App Ops chip when QQS--> - <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen> - <!-- Margin between icons of Ongoing App Ops chip when QS--> - <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen> + <!-- Margin between icons of Ongoing App Ops chip --> + <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen> <!-- Icon size of Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen> + <dimen name="ongoing_appops_chip_icon_size">16dp</dimen> <!-- Radius of Ongoing App Ops chip corners --> <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen> @@ -1358,4 +1354,11 @@ <dimen name="rounded_slider_icon_size">24dp</dimen> <!-- rounded_slider_icon_size / 2 --> <dimen name="rounded_slider_icon_inset">12dp</dimen> + + <dimen name="toast_width">296dp</dimen> + <item name="toast_icon_alpha" format="float" type="dimen">1</item> + <dimen name="toast_text_size">14sp</dimen> + <dimen name="toast_y_offset">48dp</dimen> + <dimen name="toast_icon_size">48dp</dimen> + <dimen name="toast_bg_radius">28dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index ded8a2e66aea..6196e4a6613a 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -39,4 +39,6 @@ <!-- People Tile flag --> <bool name="flag_conversations">false</bool> + + <bool name="flag_toast_style">false</bool> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7c72548a7252..afdf23b14a7a 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -764,6 +764,7 @@ <style name="TextAppearance.PrivacyDialog"> <item name="android:textSize">14sp</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="UdfpsProgressBarStyle" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 7f04f28198c0..72e4061829fa 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -32,8 +32,29 @@ public class PipSurfaceTransactionHelper { private final Matrix mTmpTransform = new Matrix(); private final float[] mTmpFloat9 = new float[9]; private final RectF mTmpSourceRectF = new RectF(); + private final RectF mTmpDestinationRectF = new RectF(); private final Rect mTmpDestinationRect = new Rect(); + public void scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, Rect destinationBounds) { + mTmpSourceRectF.set(sourceBounds); + mTmpDestinationRectF.set(destinationBounds); + mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9) + .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top); + } + + public void scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, Rect destinationBounds, + float degree, float positionX, float positionY) { + mTmpSourceRectF.set(sourceBounds); + mTmpDestinationRectF.set(destinationBounds); + mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + mTmpTransform.postRotate(degree, 0, 0); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9) + .setPosition(leash, positionX, positionY); + } + public void scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets) { mTmpSourceRectF.set(sourceBounds); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index 186379af4b1d..f6b239e31e99 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -43,17 +43,6 @@ public class Task { public static final String TAG = "Task"; - /* Task callbacks */ - @Deprecated - public interface TaskCallbacks { - /* Notifies when a task has been bound */ - void onTaskDataLoaded(Task task, ThumbnailData thumbnailData); - /* Notifies when a task has been unbound */ - void onTaskDataUnloaded(); - /* Notifies when a task's windowing mode has changed. */ - void onTaskWindowingModeChanged(); - } - /** * The Task Key represents the unique primary key for the task */ @@ -209,12 +198,6 @@ public class Task { public TaskKey key; /** - * The temporary sort index in the stack, used when ordering the stack. - */ - @Deprecated - public int temporarySortIndexInStack; - - /** * The icon is the task description icon (if provided), which falls back to the activity icon, * which can then fall back to the application icon. */ @@ -229,45 +212,24 @@ public class Task { public int colorPrimary; @ViewDebug.ExportedProperty(category="recents") public int colorBackground; - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean useLightOnPrimaryColor; /** * The task description for this task, only used to reload task icons. */ public TaskDescription taskDescription; - /** - * The state isLaunchTarget will be set for the correct task upon launching Recents. - */ - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean isLaunchTarget; - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean isStackTask; - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean isSystemApp; @ViewDebug.ExportedProperty(category="recents") public boolean isDockable; - /** - * Resize mode. See {@link ActivityInfo#resizeMode}. - */ - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public int resizeMode; - @ViewDebug.ExportedProperty(category="recents") public ComponentName topActivity; @ViewDebug.ExportedProperty(category="recents") public boolean isLocked; - @Deprecated - private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>(); + // Last snapshot data, only used for recent tasks + public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData(); public Task() { // Do nothing @@ -289,6 +251,15 @@ public class Task { this.taskDescription = new TaskDescription(); } + public Task(Task other) { + this(other.key, other.colorPrimary, other.colorBackground, other.isDockable, + other.isLocked, other.taskDescription, other.topActivity); + } + + /** + * Use {@link Task#Task(Task)}. + */ + @Deprecated public Task(TaskKey key, int colorPrimary, int colorBackground, boolean isDockable, boolean isLocked, TaskDescription taskDescription, ComponentName topActivity) { @@ -301,103 +272,6 @@ public class Task { this.topActivity = topActivity; } - @Deprecated - public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title, - String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget, - boolean isStackTask, boolean isSystemApp, boolean isDockable, - TaskDescription taskDescription, int resizeMode, ComponentName topActivity, - boolean isLocked) { - this.key = key; - this.icon = icon; - this.thumbnail = thumbnail; - this.title = title; - this.titleDescription = titleDescription; - this.colorPrimary = colorPrimary; - this.colorBackground = colorBackground; - this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary, - Color.WHITE) > 3f; - this.taskDescription = taskDescription; - this.isLaunchTarget = isLaunchTarget; - this.isStackTask = isStackTask; - this.isSystemApp = isSystemApp; - this.isDockable = isDockable; - this.resizeMode = resizeMode; - this.topActivity = topActivity; - this.isLocked = isLocked; - } - - /** - * Copies the metadata from another task, but retains the current callbacks. - */ - @Deprecated - public void copyFrom(Task o) { - this.key = o.key; - this.icon = o.icon; - this.thumbnail = o.thumbnail; - this.title = o.title; - this.titleDescription = o.titleDescription; - this.colorPrimary = o.colorPrimary; - this.colorBackground = o.colorBackground; - this.useLightOnPrimaryColor = o.useLightOnPrimaryColor; - this.taskDescription = o.taskDescription; - this.isLaunchTarget = o.isLaunchTarget; - this.isStackTask = o.isStackTask; - this.isSystemApp = o.isSystemApp; - this.isDockable = o.isDockable; - this.resizeMode = o.resizeMode; - this.isLocked = o.isLocked; - this.topActivity = o.topActivity; - } - - /** - * Add a callback. - */ - @Deprecated - public void addCallback(TaskCallbacks cb) { - if (!mCallbacks.contains(cb)) { - mCallbacks.add(cb); - } - } - - /** - * Remove a callback. - */ - @Deprecated - public void removeCallback(TaskCallbacks cb) { - mCallbacks.remove(cb); - } - - /** Updates the task's windowing mode. */ - @Deprecated - public void setWindowingMode(int windowingMode) { - key.setWindowingMode(windowingMode); - int callbackCount = mCallbacks.size(); - for (int i = 0; i < callbackCount; i++) { - mCallbacks.get(i).onTaskWindowingModeChanged(); - } - } - - /** Notifies the callback listeners that this task has been loaded */ - @Deprecated - public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) { - this.icon = applicationIcon; - this.thumbnail = thumbnailData; - int callbackCount = mCallbacks.size(); - for (int i = 0; i < callbackCount; i++) { - mCallbacks.get(i).onTaskDataLoaded(this, thumbnailData); - } - } - - /** Notifies the callback listeners that this task has been unloaded */ - @Deprecated - public void notifyTaskDataUnloaded(Drawable defaultApplicationIcon) { - icon = defaultApplicationIcon; - thumbnail = null; - for (int i = mCallbacks.size() - 1; i >= 0; i--) { - mCallbacks.get(i).onTaskDataUnloaded(); - } - } - /** * Returns the top activity component. */ @@ -424,9 +298,6 @@ public class Task { if (!isDockable) { writer.print(" dockable=N"); } - if (isLaunchTarget) { - writer.print(" launchTarget=Y"); - } if (isLocked) { writer.print(" locked=Y"); } diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index caaee5fd3f37..176562799838 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -49,7 +49,6 @@ import android.widget.TextView; import androidx.annotation.StyleRes; -import com.android.settingslib.Utils; import com.android.settingslib.graph.ThemedBatteryDrawable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.DarkIconDispatcher; @@ -105,11 +104,6 @@ public class BatteryMeterView extends LinearLayout implements private DualToneHandler mDualToneHandler; private int mUser; - /** - * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings. - */ - private boolean mUseWallpaperTextColors; - private int mNonAdaptedSingleToneColor; private int mNonAdaptedForegroundColor; private int mNonAdaptedBackgroundColor; @@ -242,31 +236,6 @@ public class BatteryMeterView extends LinearLayout implements mIsSubscribedForTunerUpdates = false; } - /** - * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll - * revert back to dark-mode-based/tinted colors. - * - * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all - * components - */ - public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) { - if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) { - return; - } - - mUseWallpaperTextColors = shouldUseWallpaperTextColor; - - if (mUseWallpaperTextColors) { - updateColors( - Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor), - Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColorSecondary), - Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor)); - } else { - updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, - mNonAdaptedSingleToneColor); - } - } - public void setColorsFromContext(Context context) { if (context == null) { return; @@ -476,13 +445,19 @@ public class BatteryMeterView extends LinearLayout implements mNonAdaptedForegroundColor = mDualToneHandler.getFillColor(intensity); mNonAdaptedBackgroundColor = mDualToneHandler.getBackgroundColor(intensity); - if (!mUseWallpaperTextColors) { - updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, - mNonAdaptedSingleToneColor); - } + updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, + mNonAdaptedSingleToneColor); } - private void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) { + /** + * Sets icon and text colors. This will be overridden by {@code onDarkChanged} events, + * if registered. + * + * @param foregroundColor + * @param backgroundColor + * @param singleToneColor + */ + public void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) { mDrawable.setColors(foregroundColor, backgroundColor, singleToneColor); mTextColor = singleToneColor; if (mBatteryPercentView != null) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 84dd25963a15..f3726a37bb65 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -16,6 +16,11 @@ package com.android.systemui.dagger; +import android.content.Context; + +import com.android.systemui.SystemUIFactory; +import com.android.systemui.tv.TvWMComponent; +import com.android.systemui.wmshell.TvWMShellModule; import com.android.systemui.wmshell.WMShellModule; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.ShellInit; @@ -34,7 +39,13 @@ import java.util.Optional; import dagger.Subcomponent; /** - * Dagger Subcomponent for WindowManager. + * Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported + * from the WM component into the SysUI component (in + * {@link SystemUIFactory#init(Context, boolean)}), and references the specific dependencies + * provided by its particular device/form-factor SystemUI implementation. + * + * ie. {@link WMComponent} includes {@link WMShellModule} + * and {@link TvWMComponent} includes {@link TvWMShellModule} */ @WMSingleton @Subcomponent(modules = {WMShellModule.class}) diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java index 4c96de232810..553b6d8679db 100644 --- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java @@ -163,7 +163,8 @@ public class RingtonePlayer extends SystemUI { } @Override - public void setPlaybackProperties(IBinder token, float volume, boolean looping) { + public void setPlaybackProperties(IBinder token, float volume, boolean looping, + boolean hapticGeneratorEnabled) { Client client; synchronized (mClients) { client = mClients.get(token); @@ -171,6 +172,7 @@ public class RingtonePlayer extends SystemUI { if (client != null) { client.mRingtone.setVolume(volume); client.mRingtone.setLooping(looping); + client.mRingtone.setHapticGeneratorEnabled(hapticGeneratorEnabled); } // else no client for token when setting playback properties but will be set at play() } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 870e714ee24c..bdd37fc521fa 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -16,11 +16,11 @@ package com.android.systemui.privacy import android.content.Context import android.util.AttributeSet -import android.view.Gravity import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout +import com.android.settingslib.Utils import com.android.systemui.R class OngoingPrivacyChip @JvmOverloads constructor( @@ -30,26 +30,13 @@ class OngoingPrivacyChip @JvmOverloads constructor( defStyleRes: Int = 0 ) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) { - private val iconMarginExpanded = context.resources.getDimensionPixelSize( - R.dimen.ongoing_appops_chip_icon_margin_expanded) - private val iconMarginCollapsed = context.resources.getDimensionPixelSize( - R.dimen.ongoing_appops_chip_icon_margin_collapsed) - private val iconSize = - context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) - private val iconColor = context.resources.getColor( - R.color.status_bar_clock_color, context.theme) - private val sidePadding = - context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) - private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg) + private var iconMargin = 0 + private var iconSize = 0 + private var iconColor = 0 + private var defaultBackgroundColor = 0 + private var cameraBackgroundColor = 0 + private lateinit var iconsContainer: LinearLayout - private lateinit var back: FrameLayout - var expanded = false - set(value) { - if (value != field) { - field = value - updateView(PrivacyChipBuilder(context, privacyList)) - } - } var privacyList = emptyList<PrivacyItem>() set(value) { @@ -60,15 +47,13 @@ class OngoingPrivacyChip @JvmOverloads constructor( override fun onFinishInflate() { super.onFinishInflate() - back = requireViewById(R.id.background) iconsContainer = requireViewById(R.id.icons_container) + + updateResources() } // Should only be called if the builder icons or app changed private fun updateView(builder: PrivacyChipBuilder) { - back.background = if (expanded) backgroundDrawable else null - val padding = if (expanded) sidePadding else 0 - back.setPaddingRelative(padding, 0, padding, 0) fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) { iconsContainer.removeAllViews() chipBuilder.generateIcons().forEachIndexed { i, it -> @@ -81,7 +66,7 @@ class OngoingPrivacyChip @JvmOverloads constructor( iconsContainer.addView(image, iconSize, iconSize) if (i != 0) { val lp = image.layoutParams as MarginLayoutParams - lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed + lp.marginStart = iconMargin image.layoutParams = lp } } @@ -90,10 +75,11 @@ class OngoingPrivacyChip @JvmOverloads constructor( if (!privacyList.isEmpty()) { generateContentDescription(builder) setIcons(builder, iconsContainer) - val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams - lp.gravity = Gravity.CENTER_VERTICAL or - (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END) - iconsContainer.layoutParams = lp + if (builder.types.contains(PrivacyType.TYPE_CAMERA)) { + iconsContainer.background.setTint(cameraBackgroundColor) + } else { + iconsContainer.background.setTint(defaultBackgroundColor) + } } else { iconsContainer.removeAllViews() } @@ -105,4 +91,20 @@ class OngoingPrivacyChip @JvmOverloads constructor( contentDescription = context.getString( R.string.ongoing_privacy_chip_content_multiple_apps, typesText) } + + private fun updateResources() { + iconMargin = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) + iconSize = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) + iconColor = + Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) + defaultBackgroundColor = context.getColor(R.color.privacy_circle_microphone_location) + cameraBackgroundColor = context.getColor(R.color.privacy_circle_camera) + + val padding = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) + iconsContainer.setPaddingRelative(padding, 0, padding, 0) + iconsContainer.background = context.getDrawable(R.drawable.privacy_chip_bg) + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 4248cf2efcd0..87252ff2b908 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -29,7 +29,6 @@ import android.media.AudioManager; import android.util.AttributeSet; import android.util.MathUtils; import android.util.Pair; -import android.view.ContextThemeWrapper; import android.view.DisplayCutout; import android.view.View; import android.view.ViewGroup; @@ -48,7 +47,6 @@ import androidx.lifecycle.LifecycleRegistry; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; -import com.android.systemui.DualToneHandler; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.privacy.OngoingPrivacyChip; @@ -75,8 +73,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn protected QuickQSPanel mHeaderQsPanel; private TouchAnimator mStatusIconsAlphaAnimator; private TouchAnimator mHeaderTextContainerAlphaAnimator; - private TouchAnimator mPrivacyChipAlphaAnimator; - private DualToneHandler mDualToneHandler; private View mSystemIconsView; private View mQuickQsStatusIcons; @@ -111,8 +107,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); - mDualToneHandler = new DualToneHandler( - new ContextThemeWrapper(context, R.style.QSHeaderTheme)); } @Override @@ -150,10 +144,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn } void onAttach(TintedIconManager iconManager) { - int colorForeground = Utils.getColorAttrDefaultColor(getContext(), - android.R.attr.colorForeground); - float intensity = getColorIntensity(colorForeground); - int fillColor = mDualToneHandler.getSingleColor(intensity); + int fillColor = Utils.getColorAttrDefaultColor(getContext(), + android.R.attr.textColorPrimary); // Set the correct tint for the status icons so they contrast iconManager.setTint(fillColor); @@ -272,19 +264,16 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); if (textColor != mTextColorPrimary) { + int textColorSecondary = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorSecondary); mTextColorPrimary = textColor; mClockView.setTextColor(textColor); - - float intensity = getColorIntensity(textColor); - int fillColor = mDualToneHandler.getSingleColor(intensity); - - Rect tintArea = new Rect(0, 0, 0, 0); - mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor); + mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary, + mTextColorPrimary); } updateStatusIconAlphaAnimator(); updateHeaderTextContainerAlphaAnimator(); - updatePrivacyChipAlphaAnimator(); } private void updateStatusIconAlphaAnimator() { @@ -299,12 +288,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn .build(); } - private void updatePrivacyChipAlphaAnimator() { - mPrivacyChipAlphaAnimator = new TouchAnimator.Builder() - .addFloat(mPrivacyChip, "alpha", 1, 0, 1) - .build(); - } - /** */ public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) { if (mExpanded == expanded) return; @@ -344,10 +327,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn mHeaderTextContainerView.setVisibility(INVISIBLE); } } - if (mPrivacyChipAlphaAnimator != null) { - mPrivacyChip.setExpanded(expansionFraction > 0.5); - mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction); - } mKeyguardExpansionFraction = keyguardExpansionFraction; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java index 7d8d86fe691e..eddcf8c1e9ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java @@ -28,9 +28,7 @@ import android.widget.TextView; import com.android.settingslib.Utils; import com.android.settingslib.graph.SignalDrawable; -import com.android.systemui.DualToneHandler; import com.android.systemui.R; -import com.android.systemui.qs.QuickStatusBarHeader; import java.util.Objects; @@ -40,9 +38,6 @@ public class QSCarrier extends LinearLayout { private TextView mCarrierText; private ImageView mMobileSignal; private ImageView mMobileRoaming; - private DualToneHandler mDualToneHandler; - private ColorStateList mColorForegroundStateList; - private float mColorForegroundIntensity; private CellSignalState mLastSignalState; public QSCarrier(Context context) { @@ -64,7 +59,6 @@ public class QSCarrier extends LinearLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); - mDualToneHandler = new DualToneHandler(getContext()); mMobileGroup = findViewById(R.id.mobile_combo); if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { mMobileRoaming = findViewById(R.id.mobile_roaming_large); @@ -74,11 +68,6 @@ public class QSCarrier extends LinearLayout { mMobileSignal = findViewById(R.id.mobile_signal); mCarrierText = findViewById(R.id.qs_carrier_text); mMobileSignal.setImageDrawable(new SignalDrawable(mContext)); - - int colorForeground = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.colorForeground); - mColorForegroundStateList = ColorStateList.valueOf(colorForeground); - mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground); } /** @@ -92,8 +81,8 @@ public class QSCarrier extends LinearLayout { mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE); if (state.visible) { mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); - ColorStateList colorStateList = ColorStateList.valueOf( - mDualToneHandler.getSingleColor(mColorForegroundIntensity)); + ColorStateList colorStateList = Utils.getColorAttr(mContext, + android.R.attr.textColorPrimary); mMobileRoaming.setImageTintList(colorStateList); mMobileSignal.setImageTintList(colorStateList); mMobileSignal.setImageLevel(state.mobileSignalIconId); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 543874325254..26781f4ccf09 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -277,12 +277,12 @@ public class ScreenMediaRecorder { */ void end() { mMediaRecorder.stop(); - mMediaProjection.stop(); mMediaRecorder.release(); - mMediaRecorder = null; - mMediaProjection = null; mInputSurface.release(); mVirtualDisplay.release(); + mMediaProjection.stop(); + mMediaRecorder = null; + mMediaProjection = null; stopInternalAudioRecording(); Log.d(TAG, "end recording"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 2dd85e9bb98d..778f813b8d07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -74,4 +74,8 @@ public class FeatureFlags { public boolean isPeopleTileEnabled() { return mFlagReader.isEnabled(R.bool.flag_conversations); } + + public boolean isToastStyleEnabled() { + return mFlagReader.isEnabled(R.bool.flag_toast_style); + } } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index e357577f88c8..78639147a375 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -15,12 +15,15 @@ */ package com.android.systemui.theme; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManager; +import android.content.om.OverlayManagerTransaction; import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -229,49 +232,53 @@ public class ThemeOverlayApplier implements Dumpable { final List<OverlayInfo> overlays = new ArrayList<>(); targetPackagesToQuery.forEach(targetPackage -> overlays.addAll(mOverlayManager .getOverlayInfosForTarget(targetPackage, UserHandle.SYSTEM))); - final Map<String, String> overlaysToDisable = overlays.stream() + final List<Pair<String, String>> overlaysToDisable = overlays.stream() .filter(o -> mTargetPackageToCategories.get(o.targetPackageName).contains(o.category)) .filter(o -> overlayCategoriesToDisable.contains(o.category)) .filter(o -> o.isEnabled()) - .collect(Collectors.toMap((o) -> o.category, (o) -> o.packageName)); + .map(o -> new Pair<>(o.category, o.packageName)) + .collect(Collectors.toList()); + OverlayManagerTransaction.Builder transaction = getTransactionBuilder(); // Toggle overlays in the order of THEME_CATEGORIES. for (String category : THEME_CATEGORIES) { if (categoryToPackage.containsKey(category)) { - setEnabled(categoryToPackage.get(category), category, userHandles, true); - } else if (overlaysToDisable.containsKey(category)) { - setEnabled(overlaysToDisable.get(category), category, userHandles, false); + OverlayIdentifier overlayInfo = + new OverlayIdentifier(categoryToPackage.get(category)); + setEnabled(transaction, overlayInfo, category, userHandles, true); } } + for (Pair<String, String> packageToDisable : overlaysToDisable) { + OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second); + setEnabled(transaction, overlayInfo, packageToDisable.first, userHandles, false); + } + + mExecutor.execute(() -> { + mOverlayManager.commit(transaction.build()); + }); + } + + @VisibleForTesting + protected OverlayManagerTransaction.Builder getTransactionBuilder() { + return new OverlayManagerTransaction.Builder(); } - private void setEnabled( - String packageName, String category, Set<UserHandle> handles, boolean enabled) { + private void setEnabled(OverlayManagerTransaction.Builder transaction, + OverlayIdentifier identifier, String category, Set<UserHandle> handles, + boolean enabled) { + if (DEBUG) { + Log.d(TAG, "setEnabled: " + identifier.getPackageName() + " category: " + + category + ": " + enabled); + } for (UserHandle userHandle : handles) { - setEnabledAsync(packageName, userHandle, enabled); + transaction.setEnabled(identifier, enabled, userHandle.getIdentifier()); } if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) { - setEnabledAsync(packageName, UserHandle.SYSTEM, enabled); + transaction.setEnabled(identifier, enabled, UserHandle.SYSTEM.getIdentifier()); } } - private void setEnabledAsync(String pkg, UserHandle userHandle, boolean enabled) { - mExecutor.execute(() -> { - if (DEBUG) Log.d(TAG, String.format("setEnabled: %s %s %b", pkg, userHandle, enabled)); - try { - if (enabled) { - mOverlayManager.setEnabledExclusiveInCategory(pkg, userHandle); - } else { - mOverlayManager.setEnabled(pkg, false, userHandle); - } - } catch (SecurityException | IllegalStateException e) { - Log.e(TAG, - String.format("setEnabled failed: %s %s %b", pkg, userHandle, enabled), e); - } - }); - } - /** * @inherit */ diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index e9fcf1aa9598..365cd2a5d20b 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -20,10 +20,21 @@ import android.animation.Animator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.util.Log; +import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; import android.widget.ToastPresenter; import com.android.internal.R; +import com.android.launcher3.icons.IconFactory; import com.android.systemui.plugins.ToastPlugin; /** @@ -35,23 +46,43 @@ public class SystemUIToast implements ToastPlugin.Toast { final CharSequence mText; final ToastPlugin.Toast mPluginToast; - final int mDefaultGravity; - final int mDefaultY; + private final String mPackageName; + private final int mUserId; + private final LayoutInflater mLayoutInflater; + private final boolean mToastStyleEnabled; + final int mDefaultX = 0; final int mDefaultHorizontalMargin = 0; final int mDefaultVerticalMargin = 0; - SystemUIToast(Context context, CharSequence text) { - this(context, text, null); + private int mDefaultY; + private int mDefaultGravity; + + @NonNull private final View mToastView; + @Nullable private final Animator mInAnimator; + @Nullable private final Animator mOutAnimator; + + SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, + String packageName, int userId, boolean toastStyleEnabled, int orientation) { + this(layoutInflater, context, text, null, packageName, userId, + toastStyleEnabled, orientation); } - SystemUIToast(Context context, CharSequence text, ToastPlugin.Toast pluginToast) { + SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, + ToastPlugin.Toast pluginToast, String packageName, int userId, + boolean toastStyleEnabled, int orientation) { + mToastStyleEnabled = toastStyleEnabled; + mLayoutInflater = layoutInflater; mContext = context; mText = text; mPluginToast = pluginToast; + mPackageName = packageName; + mUserId = userId; + mToastView = inflateToastView(); + mInAnimator = createInAnimator(); + mOutAnimator = createOutAnimator(); - mDefaultGravity = context.getResources().getInteger(R.integer.config_toastDefaultGravity); - mDefaultY = context.getResources().getDimensionPixelSize(R.dimen.toast_y_offset); + onOrientationChange(orientation); } @Override @@ -102,28 +133,19 @@ public class SystemUIToast implements ToastPlugin.Toast { @Override @NonNull public View getView() { - if (isPluginToast() && mPluginToast.getView() != null) { - return mPluginToast.getView(); - } - return ToastPresenter.getTextToastView(mContext, mText); + return mToastView; } @Override @Nullable public Animator getInAnimation() { - if (isPluginToast() && mPluginToast.getInAnimation() != null) { - return mPluginToast.getInAnimation(); - } - return null; + return mInAnimator; } @Override @Nullable public Animator getOutAnimation() { - if (isPluginToast() && mPluginToast.getOutAnimation() != null) { - return mPluginToast.getOutAnimation(); - } - return null; + return mOutAnimator; } /** @@ -136,4 +158,80 @@ public class SystemUIToast implements ToastPlugin.Toast { private boolean isPluginToast() { return mPluginToast != null; } + + private View inflateToastView() { + if (isPluginToast() && mPluginToast.getView() != null) { + return mPluginToast.getView(); + } + + View toastView; + if (mToastStyleEnabled) { + toastView = mLayoutInflater.inflate( + com.android.systemui.R.layout.text_toast, null); + ((TextView) toastView.findViewById(com.android.systemui.R.id.text)).setText(mText); + + ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon)) + .setImageDrawable(getBadgedIcon(mContext, mPackageName, mUserId)); + } else { + toastView = ToastPresenter.getTextToastView(mContext, mText); + } + + return toastView; + } + + /** + * Called on orientation changes to update parameters associated with the toast placement. + */ + public void onOrientationChange(int orientation) { + if (mPluginToast != null) { + mPluginToast.onOrientationChange(orientation); + } + + mDefaultY = mContext.getResources().getDimensionPixelSize( + mToastStyleEnabled + ? com.android.systemui.R.dimen.toast_y_offset + : R.dimen.toast_y_offset); + mDefaultGravity = + mContext.getResources().getInteger(R.integer.config_toastDefaultGravity); + } + + private Animator createInAnimator() { + if (isPluginToast() && mPluginToast.getInAnimation() != null) { + return mPluginToast.getInAnimation(); + } + + return mToastStyleEnabled + ? ToastDefaultAnimation.Companion.toastIn(getView()) + : null; + } + + private Animator createOutAnimator() { + if (isPluginToast() && mPluginToast.getOutAnimation() != null) { + return mPluginToast.getOutAnimation(); + } + return mToastStyleEnabled + ? ToastDefaultAnimation.Companion.toastOut(getView()) + : null; + } + + /** + * Get badged app icon if necessary, similar as used in the Settings UI. + * @return The icon to use + */ + public static Drawable getBadgedIcon(@NonNull Context context, String packageName, + int userId) { + final PackageManager packageManager = context.getPackageManager(); + try { + final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userId); + UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid); + IconFactory iconFactory = IconFactory.obtain(context); + Bitmap iconBmp = iconFactory.createBadgedIconBitmap( + appInfo.loadUnbadgedIcon(packageManager), user, false).icon; + return new BitmapDrawable(context.getResources(), iconBmp); + } catch (PackageManager.NameNotFoundException e) { + Log.e("SystemUIToast", "could not load icon for package=" + packageName + " e=" + e); + return null; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt new file mode 100644 index 000000000000..603d69057e5c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.toast + +import android.animation.ObjectAnimator +import android.view.View +import android.view.animation.LinearInterpolator +import android.view.animation.PathInterpolator +import android.animation.AnimatorSet + +class ToastDefaultAnimation { + /** + * sum of the in and out animation durations cannot exceed + * [com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER] to prevent the toast + * window from being removed before animations are completed + */ + companion object { + // total duration shouldn't exceed NotificationManagerService's delay for "in" animation + fun toastIn(view: View): AnimatorSet? { + val icon: View? = view.findViewById(com.android.systemui.R.id.icon) + val text: View? = view.findViewById(com.android.systemui.R.id.text) + if (icon == null || text == null) { + return null + } + val linearInterp = LinearInterpolator() + val scaleInterp = PathInterpolator(0f, 0f, 0f, 1f) + val sX = ObjectAnimator.ofFloat(view, "scaleX", 0.9f, 1f).apply { + interpolator = scaleInterp + duration = 333 + } + val sY = ObjectAnimator.ofFloat(view, "scaleY", 0.9f, 1f).apply { + interpolator = scaleInterp + duration = 333 + } + val vA = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply { + interpolator = linearInterp + duration = 66 + } + text.alpha = 0f // Set now otherwise won't apply until start delay + val tA = ObjectAnimator.ofFloat(text, "alpha", 0f, 1f).apply { + interpolator = linearInterp + duration = 283 + startDelay = 50 + } + icon.alpha = 0f // Set now otherwise won't apply until start delay + val iA = ObjectAnimator.ofFloat(icon, "alpha", 0f, 1f).apply { + interpolator = linearInterp + duration = 283 + startDelay = 50 + } + return AnimatorSet().apply { + playTogether(sX, sY, vA, tA, iA) + } + } + + fun toastOut(view: View): AnimatorSet? { + // total duration shouldn't exceed NotificationManagerService's delay for "out" anim + val icon: View? = view.findViewById(com.android.systemui.R.id.icon) + val text: View? = view.findViewById(com.android.systemui.R.id.text) + if (icon == null || text == null) { + return null + } + val linearInterp = LinearInterpolator() + val scaleInterp = PathInterpolator(0.3f, 0f, 1f, 1f) + val sX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.9f).apply { + interpolator = scaleInterp + duration = 250 + } + val sY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.9f).apply { + interpolator = scaleInterp + duration = 250 + } + val vA = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply { + interpolator = linearInterp + duration = 100 + startDelay = 150 + } + val tA = ObjectAnimator.ofFloat(text, "alpha", 1f, 0f).apply { + interpolator = linearInterp + duration = 166 + } + val iA = ObjectAnimator.ofFloat(icon, "alpha", 1f, 0f).apply { + interpolator = linearInterp + duration = 166 + } + return AnimatorSet().apply { + playTogether(sX, sY, vA, tA, iA) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java index d8cb61c6b349..8b782d4b7923 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java @@ -17,6 +17,7 @@ package com.android.systemui.toast; import android.content.Context; +import android.view.LayoutInflater; import androidx.annotation.NonNull; @@ -26,6 +27,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.ToastPlugin; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -40,10 +42,18 @@ import javax.inject.Inject; public class ToastFactory implements Dumpable { // only one ToastPlugin can be connected at a time. private ToastPlugin mPlugin; + private final LayoutInflater mLayoutInflater; + private final boolean mToastStyleEnabled; @Inject - public ToastFactory(PluginManager pluginManager, DumpManager dumpManager) { + public ToastFactory( + LayoutInflater layoutInflater, + PluginManager pluginManager, + DumpManager dumpManager, + FeatureFlags featureFlags) { + mLayoutInflater = layoutInflater; dumpManager.registerDumpable("ToastFactory", this); + mToastStyleEnabled = featureFlags.isToastStyleEnabled(); pluginManager.addPluginListener( new PluginListener<ToastPlugin>() { @Override @@ -64,11 +74,13 @@ public class ToastFactory implements Dumpable { * Create a toast to be shown by ToastUI. */ public SystemUIToast createToast(Context context, CharSequence text, String packageName, - int userId) { + int userId, int orientation) { if (isPluginAvailable()) { - return new SystemUIToast(context, text, mPlugin.createToast(text, packageName, userId)); + return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text, + packageName, userId), packageName, userId, mToastStyleEnabled, orientation); } - return new SystemUIToast(context, text); + return new SystemUIToast(mLayoutInflater, context, text, packageName, userId, + mToastStyleEnabled, orientation); } private boolean isPluginAvailable() { @@ -79,5 +91,6 @@ public class ToastFactory implements Dumpable { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("ToastFactory:"); pw.println(" mAttachedPlugin=" + mPlugin); + pw.println(" mToastStyleEnabled=" + mToastStyleEnabled); } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt index 78173cf62a93..51541bd3032e 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt @@ -49,6 +49,15 @@ class ToastLogger @Inject constructor( }) } + fun logOrientationChange(text: String, isPortrait: Boolean) { + log(DEBUG, { + str1 = text + bool1 = isPortrait + }, { + "Orientation change for toast. msg=\'$str1\' isPortrait=$bool1" + }) + } + private inline fun log( logLevel: LogLevel, initializer: LogMessage.() -> Unit, diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 409d1361223c..92ea1d0e5fbd 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -16,27 +16,28 @@ package com.android.systemui.toast; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + import android.animation.Animator; import android.annotation.MainThread; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.INotificationManager; import android.app.ITransientNotificationCallback; import android.content.Context; +import android.content.res.Configuration; import android.os.IBinder; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; -import android.widget.Toast; import android.widget.ToastPresenter; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.Objects; @@ -58,18 +59,19 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { private final IAccessibilityManager mIAccessibilityManager; private final AccessibilityManager mAccessibilityManager; private final ToastFactory mToastFactory; - private final DelayableExecutor mMainExecutor; private final ToastLogger mToastLogger; private SystemUIToast mToast; @Nullable private ToastPresenter mPresenter; @Nullable private ITransientNotificationCallback mCallback; + private ToastOutAnimatorListener mToastOutAnimatorListener; + + private int mOrientation = ORIENTATION_PORTRAIT; @Inject public ToastUI( Context context, CommandQueue commandQueue, ToastFactory toastFactory, - @Main DelayableExecutor mainExecutor, ToastLogger toastLogger) { this(context, commandQueue, INotificationManager.Stub.asInterface( @@ -77,21 +79,19 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { IAccessibilityManager.Stub.asInterface( ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)), toastFactory, - mainExecutor, toastLogger); } @VisibleForTesting ToastUI(Context context, CommandQueue commandQueue, INotificationManager notificationManager, @Nullable IAccessibilityManager accessibilityManager, - ToastFactory toastFactory, DelayableExecutor mainExecutor, ToastLogger toastLogger + ToastFactory toastFactory, ToastLogger toastLogger ) { super(context); mCommandQueue = commandQueue; mNotificationManager = notificationManager; mIAccessibilityManager = accessibilityManager; mToastFactory = toastFactory; - mMainExecutor = mainExecutor; mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mToastLogger = toastLogger; } @@ -105,36 +105,38 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { @MainThread public void showToast(int uid, String packageName, IBinder token, CharSequence text, IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) { - if (mPresenter != null) { - hideCurrentToast(); - } - UserHandle userHandle = UserHandle.getUserHandleForUid(uid); - Context context = mContext.createContextAsUser(userHandle, 0); - mToast = mToastFactory.createToast(context, text, packageName, userHandle.getIdentifier()); + Runnable showToastRunnable = () -> { + UserHandle userHandle = UserHandle.getUserHandleForUid(uid); + Context context = mContext.createContextAsUser(userHandle, 0); + mToast = mToastFactory.createToast(mContext /* sysuiContext */, text, packageName, + userHandle.getIdentifier(), mOrientation); - if (mToast.hasCustomAnimation()) { if (mToast.getInAnimation() != null) { mToast.getInAnimation().start(); } - final Animator hideAnimator = mToast.getOutAnimation(); - if (hideAnimator != null) { - final long durationMillis = duration == Toast.LENGTH_LONG - ? TOAST_LONG_TIME : TOAST_SHORT_TIME; - final long updatedDuration = mAccessibilityManager.getRecommendedTimeoutMillis( - (int) durationMillis, AccessibilityManager.FLAG_CONTENT_TEXT); - mMainExecutor.executeDelayed(() -> hideAnimator.start(), - updatedDuration - hideAnimator.getTotalDuration()); - } + + mCallback = callback; + mPresenter = new ToastPresenter(context, mIAccessibilityManager, + mNotificationManager, packageName); + // Set as trusted overlay so touches can pass through toasts + mPresenter.getLayoutParams().setTrustedOverlay(); + mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString()); + mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(), + mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(), + mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation()); + }; + + if (mToastOutAnimatorListener != null) { + // if we're currently animating out a toast, show new toast after prev toast is hidden + mToastOutAnimatorListener.setShowNextToastRunnable(showToastRunnable); + } else if (mPresenter != null) { + // if there's a toast already showing that we haven't tried hiding yet, hide it and + // then show the next toast after its hidden animation is done + hideCurrentToast(showToastRunnable); + } else { + // else, show this next toast immediately + showToastRunnable.run(); } - mCallback = callback; - mPresenter = new ToastPresenter(context, mIAccessibilityManager, mNotificationManager, - packageName); - // Set as trusted overlay so touches can pass through toasts - mPresenter.getLayoutParams().setTrustedOverlay(); - mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString()); - mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(), - mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(), - mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation()); } @Override @@ -146,12 +148,88 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { return; } mToastLogger.logOnHideToast(packageName, token.toString()); - hideCurrentToast(); + hideCurrentToast(null); } @MainThread - private void hideCurrentToast() { - mPresenter.hide(mCallback); + private void hideCurrentToast(Runnable runnable) { + if (mToast.getOutAnimation() != null) { + Animator animator = mToast.getOutAnimation(); + mToastOutAnimatorListener = new ToastOutAnimatorListener(mPresenter, mCallback, + runnable); + animator.addListener(mToastOutAnimatorListener); + animator.start(); + } else { + mPresenter.hide(mCallback); + if (runnable != null) { + runnable.run(); + } + } + mToast = null; mPresenter = null; + mCallback = null; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + if (newConfig.orientation != mOrientation) { + mOrientation = newConfig.orientation; + if (mToast != null) { + mToastLogger.logOrientationChange(mToast.mText.toString(), + mOrientation == ORIENTATION_PORTRAIT); + mToast.onOrientationChange(mOrientation); + mPresenter.updateLayoutParams( + mToast.getXOffset(), + mToast.getYOffset(), + mToast.getHorizontalMargin(), + mToast.getVerticalMargin(), + mToast.getGravity()); + } + } + } + + /** + * Once the out animation for a toast is finished, start showing the next toast. + */ + class ToastOutAnimatorListener implements Animator.AnimatorListener { + final ToastPresenter mPrevPresenter; + final ITransientNotificationCallback mPrevCallback; + @Nullable Runnable mShowNextToastRunnable; + + ToastOutAnimatorListener( + @NonNull ToastPresenter presenter, + @NonNull ITransientNotificationCallback callback, + @Nullable Runnable runnable) { + mPrevPresenter = presenter; + mPrevCallback = callback; + mShowNextToastRunnable = runnable; + } + + void setShowNextToastRunnable(Runnable runnable) { + mShowNextToastRunnable = runnable; + } + + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + mPrevPresenter.hide(mPrevCallback); + if (mShowNextToastRunnable != null) { + mShowNextToastRunnable.run(); + } + mToastOutAnimatorListener = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + onAnimationEnd(animation); + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 0795d89eb0bc..ff2881953342 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -19,6 +19,7 @@ package com.android.systemui.wmshell; import android.content.Context; import android.os.Handler; +import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; @@ -28,6 +29,7 @@ import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; @@ -49,7 +51,7 @@ import dagger.Module; import dagger.Provides; /** - * Dagger module for TV Pip. + * Provides TV specific dependencies for Pip. */ @Module(includes = {WMShellBaseModule.class}) public abstract class TvPipModule { @@ -143,7 +145,8 @@ public abstract class TvPipModule { PipAnimationController pipAnimationController, PipTransitionController pipTransitionController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, - Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController, + Optional<LegacySplitScreenController> splitScreenOptional, + DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index f23367b4d65b..141b9f7d410d 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -20,6 +20,7 @@ import android.animation.AnimationHandler; import android.content.Context; import android.view.IWindowManager; +import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -39,11 +40,20 @@ import dagger.Module; import dagger.Provides; /** - * Provides dependencies from {@link com.android.wm.shell} which could be customized among different - * branches of SystemUI. + * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only + * accessible from components within the WM subcomponent (can be explicitly exposed to the + * SysUIComponent, see {@link WMComponent}). + * + * This module only defines Shell dependencies for the TV SystemUI implementation. Common + * dependencies should go into {@link WMShellBaseModule}. */ @Module(includes = {TvPipModule.class}) public class TvWMShellModule { + + // + // Internal common - Components used internally by multiple shell features + // + @WMSingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, @@ -53,16 +63,20 @@ public class TvWMShellModule { transactionPool); } + // + // Split/multiwindow + // + @WMSingleton @Provides - static LegacySplitScreen provideSplitScreen(Context context, + static LegacySplitScreenController provideSplitScreen(Context context, DisplayController displayController, SystemWindows systemWindows, DisplayImeController displayImeController, TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener, Transitions transitions, @ShellMainThread ShellExecutor mainExecutor, @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) { - return LegacySplitScreenController.create(context, displayController, systemWindows, + return new LegacySplitScreenController(context, displayController, systemWindows, displayImeController, transactionPool, shellTaskOrganizer, syncQueue, taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 81ac21c00823..ec61db591324 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.model.SysUiState; @@ -73,7 +74,20 @@ import java.util.concurrent.Executor; import javax.inject.Inject; /** - * Proxy in SysUiScope to delegate events to controllers in WM Shell library. + * A SystemUI service that starts with the SystemUI application and sets up any bindings between + * Shell and SysUI components. This service starts happens after the {@link WMComponent} has + * already been initialized and may only reference Shell components that are explicitly exported to + * SystemUI (see {@link WMComponent}. + * + * eg. SysUI application starts + * -> SystemUIFactory is initialized + * -> WMComponent is created + * -> WMShellBaseModule dependencies are injected + * -> WMShellModule (form-factory specific) dependencies are injected + * -> SysUIComponent is created + * -> WMComponents are explicitly provided to SysUIComponent for injection into SysUI code + * -> SysUI services are started + * -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces */ @SysUISingleton public final class WMShell extends SystemUI @@ -142,6 +156,8 @@ public final class WMShell extends SystemUI @Override public void start() { + // TODO: Consider piping config change and other common calls to a shell component to + // delegate internally mProtoTracer.add(this); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index b42dde63d1c2..449db61a0fbb 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -32,6 +32,7 @@ import android.view.WindowManager; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.wm.shell.FullscreenTaskListener; @@ -45,6 +46,7 @@ import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.TaskViewFactoryController; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.apppairs.AppPairs; +import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.common.DisplayController; @@ -63,6 +65,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -70,8 +73,8 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PipAppOpsListener; +import com.android.wm.shell.pip.phone.PipController; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -85,8 +88,13 @@ import dagger.Module; import dagger.Provides; /** - * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here - * should be shared among different branches of SystemUI. + * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only + * accessible from components within the WM subcomponent (can be explicitly exposed to the + * SysUIComponent, see {@link WMComponent}). + * + * This module only defines *common* dependencies across various SystemUI implementations, + * dependencies that are device/form factor SystemUI implementation specific should go into their + * respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.) */ @Module public abstract class WMShellBaseModule { @@ -174,72 +182,65 @@ public abstract class WMShellBaseModule { } } + // + // Internal common - Components used internally by multiple shell features + // + @WMSingleton @Provides - static ShellInit provideShellInit(DisplayImeController displayImeController, - DragAndDropController dragAndDropController, - ShellTaskOrganizer shellTaskOrganizer, - Optional<LegacySplitScreen> legacySplitScreenOptional, - Optional<SplitScreenController> splitScreenOptional, - Optional<AppPairs> appPairsOptional, - FullscreenTaskListener fullscreenTaskListener, - Transitions transitions, - @ShellMainThread ShellExecutor mainExecutor) { - return ShellInitImpl.create(displayImeController, - dragAndDropController, - shellTaskOrganizer, - legacySplitScreenOptional, - splitScreenOptional, - appPairsOptional, - fullscreenTaskListener, - transitions, - mainExecutor); + static DisplayController provideDisplayController(Context context, + IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) { + return new DisplayController(context, wmService, mainExecutor); } - /** - * Note, this is only optional because we currently pass this to the SysUI component scope and - * for non-primary users, we may inject a null-optional for that dependency. - */ @WMSingleton @Provides - static Optional<ShellCommandHandler> provideShellCommandHandler( - ShellTaskOrganizer shellTaskOrganizer, - Optional<LegacySplitScreen> legacySplitScreenOptional, - Optional<SplitScreenController> splitScreenOptional, - Optional<Pip> pipOptional, - Optional<OneHanded> oneHandedOptional, - Optional<HideDisplayCutout> hideDisplayCutout, - Optional<AppPairs> appPairsOptional, + static DragAndDropController provideDragAndDropController(Context context, + DisplayController displayController) { + return new DragAndDropController(context, displayController); + } + + @WMSingleton + @Provides + static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor, + Context context, SizeCompatUIController sizeCompatUI) { + return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI); + } + + @WMSingleton + @Provides + static SizeCompatUIController provideSizeCompatUIController(Context context, + DisplayController displayController, DisplayImeController imeController, @ShellMainThread ShellExecutor mainExecutor) { - return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer, - legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional, - hideDisplayCutout, appPairsOptional, mainExecutor)); + return new SizeCompatUIController(context, displayController, imeController, mainExecutor); } @WMSingleton @Provides - static TransactionPool provideTransactionPool() { - return new TransactionPool(); + static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool, + @ShellMainThread ShellExecutor mainExecutor) { + return new SyncTransactionQueue(pool, mainExecutor); } @WMSingleton @Provides - static DisplayController provideDisplayController(Context context, - IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) { - return new DisplayController(context, wmService, mainExecutor); + static SystemWindows provideSystemWindows(DisplayController displayController, + IWindowManager wmService) { + return new SystemWindows(displayController, wmService); } + // We currently dedupe multiple messages, so we use the shell main handler directly @WMSingleton @Provides - static DragAndDropController provideDragAndDropController(Context context, - DisplayController displayController) { - return new DragAndDropController(context, displayController); + static TaskStackListenerImpl providerTaskStackListenerImpl( + @ShellMainThread Handler mainHandler) { + return new TaskStackListenerImpl(mainHandler); } @WMSingleton @Provides - static FloatingContentCoordinator provideFloatingContentCoordinator() { - return new FloatingContentCoordinator(); + static TransactionPool provideTransactionPool() { + return new TransactionPool(); } @WMSingleton @@ -249,10 +250,99 @@ public abstract class WMShellBaseModule { return new WindowManagerShellWrapper(mainExecutor); } + // + // Bubbles + // + + @WMSingleton + @Provides + static Optional<Bubbles> provideBubbles(Optional<BubbleController> bubbleController) { + return bubbleController.map((controller) -> controller.asBubbles()); + } + + // Note: Handler needed for LauncherApps.register + @WMSingleton + @Provides + static Optional<BubbleController> provideBubbleController(Context context, + FloatingContentCoordinator floatingContentCoordinator, + IStatusBarService statusBarService, + WindowManager windowManager, + WindowManagerShellWrapper windowManagerShellWrapper, + LauncherApps launcherApps, + UiEventLogger uiEventLogger, + ShellTaskOrganizer organizer, + @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler) { + return Optional.of(BubbleController.create(context, null /* synchronizer */, + floatingContentCoordinator, statusBarService, windowManager, + windowManagerShellWrapper, launcherApps, uiEventLogger, organizer, + mainExecutor, mainHandler)); + } + + // + // Fullscreen + // + + @WMSingleton + @Provides + static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) { + return new FullscreenTaskListener(syncQueue); + } + + // + // Hide display cutout + // + + @WMSingleton + @Provides + static Optional<HideDisplayCutout> provideHideDisplayCutout( + Optional<HideDisplayCutoutController> hideDisplayCutoutController) { + return hideDisplayCutoutController.map((controller) -> controller.asHideDisplayCutout()); + } + + @WMSingleton + @Provides + static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context, + DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) { + return Optional.ofNullable( + HideDisplayCutoutController.create(context, displayController, mainExecutor)); + } + + // + // One handed mode (optional feature) + // + + @WMSingleton + @Provides + static Optional<OneHanded> provideOneHanded(Optional<OneHandedController> oneHandedController) { + return oneHandedController.map((controller) -> controller.asOneHanded()); + } + + // Needs the shell main handler for ContentObserver callbacks + @WMSingleton + @Provides + static Optional<OneHandedController> provideOneHandedController(Context context, + DisplayController displayController, TaskStackListenerImpl taskStackListener, + UiEventLogger uiEventLogger, + @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler) { + return Optional.ofNullable(OneHandedController.create(context, displayController, + taskStackListener, uiEventLogger, mainExecutor, mainHandler)); + } + + // + // Pip (optional feature) + // + + @WMSingleton + @Provides + static FloatingContentCoordinator provideFloatingContentCoordinator() { + return new FloatingContentCoordinator(); + } + @WMSingleton @Provides static PipAppOpsListener providePipAppOpsListener(Context context, - IActivityManager activityManager, PipTouchHandler pipTouchHandler, @ShellMainThread ShellExecutor mainExecutor) { return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor); @@ -268,38 +358,39 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger, - PackageManager packageManager) { - return new PipUiEventLogger(uiEventLogger, packageManager); - } - - @WMSingleton - @Provides static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) { return new PipSurfaceTransactionHelper(context); } @WMSingleton @Provides - static SystemWindows provideSystemWindows(DisplayController displayController, - IWindowManager wmService) { - return new SystemWindows(displayController, wmService); + static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger, + PackageManager packageManager) { + return new PipUiEventLogger(uiEventLogger, packageManager); } + // + // Shell transitions + // + @WMSingleton @Provides - static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool, - @ShellMainThread ShellExecutor mainExecutor) { - return new SyncTransactionQueue(pool, mainExecutor); + static RemoteTransitions provideRemoteTransitions(Transitions transitions) { + return Transitions.asRemoteTransitions(transitions); } @WMSingleton @Provides - static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor, - Context context, SizeCompatUI sizeCompatUI) { - return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI); + static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, + @ShellMainThread ShellExecutor mainExecutor, + @ShellAnimationThread ShellExecutor animExecutor) { + return new Transitions(organizer, pool, mainExecutor, animExecutor); } + // + // Split/multiwindow + // + @WMSingleton @Provides static RootTaskDisplayAreaOrganizer provideRootTaskDisplayAreaOrganizer( @@ -307,17 +398,6 @@ public abstract class WMShellBaseModule { return new RootTaskDisplayAreaOrganizer(mainExecutor, context); } - // We currently dedupe multiple messages, so we use the shell main handler directly - @WMSingleton - @Provides - static TaskStackListenerImpl providerTaskStackListenerImpl( - @ShellMainThread Handler mainHandler) { - return new TaskStackListenerImpl(mainHandler); - } - - @BindsOptionalOf - abstract LegacySplitScreen optionalLegacySplitScreen(); - @WMSingleton @Provides static Optional<SplitScreen> provideSplitScreen( @@ -340,81 +420,91 @@ public abstract class WMShellBaseModule { } } - @BindsOptionalOf - abstract AppPairs optionalAppPairs(); + // Legacy split (optional feature) - // Note: Handler needed for LauncherApps.register @WMSingleton @Provides - static Optional<Bubbles> provideBubbles(Context context, - FloatingContentCoordinator floatingContentCoordinator, - IStatusBarService statusBarService, - WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, - LauncherApps launcherApps, - UiEventLogger uiEventLogger, - ShellTaskOrganizer organizer, - @ShellMainThread ShellExecutor mainExecutor, - @ShellMainThread Handler mainHandler) { - return Optional.of(BubbleController.create(context, null /* synchronizer */, - floatingContentCoordinator, statusBarService, windowManager, - windowManagerShellWrapper, launcherApps, uiEventLogger, organizer, - mainExecutor, mainHandler)); + static Optional<LegacySplitScreen> provideLegacySplitScreen( + Optional<LegacySplitScreenController> splitScreenController) { + return splitScreenController.map((controller) -> controller.asLegacySplitScreen()); } - // Needs the shell main handler for ContentObserver callbacks - @WMSingleton - @Provides - static Optional<OneHanded> provideOneHandedController(Context context, - DisplayController displayController, TaskStackListenerImpl taskStackListener, - UiEventLogger uiEventLogger, - @ShellMainThread ShellExecutor mainExecutor, - @ShellMainThread Handler mainHandler) { - return Optional.ofNullable(OneHandedController.create(context, displayController, - taskStackListener, uiEventLogger, mainExecutor, mainHandler)); - } + @BindsOptionalOf + abstract LegacySplitScreenController optionalLegacySplitScreenController(); - @WMSingleton - @Provides - static Optional<HideDisplayCutout> provideHideDisplayCutoutController(Context context, - DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) { - return Optional.ofNullable( - HideDisplayCutoutController.create(context, displayController, mainExecutor)); - } + // App Pairs (optional feature) @WMSingleton @Provides - static Optional<TaskViewFactory> provideTaskViewFactory(ShellTaskOrganizer shellTaskOrganizer, - @ShellMainThread ShellExecutor mainExecutor) { - return Optional.of(new TaskViewFactoryController(shellTaskOrganizer, mainExecutor) - .getTaskViewFactory()); + static Optional<AppPairs> provideAppPairs(Optional<AppPairsController> appPairsController) { + return appPairsController.map((controller) -> controller.asAppPairs()); } + @BindsOptionalOf + abstract AppPairsController optionalAppPairs(); + + // + // Task view factory + // + @WMSingleton @Provides - static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) { - return new FullscreenTaskListener(syncQueue); + static Optional<TaskViewFactory> provideTaskViewFactory( + TaskViewFactoryController taskViewFactoryController) { + return Optional.of(taskViewFactoryController.asTaskViewFactory()); } @WMSingleton @Provides - static RemoteTransitions provideRemoteTransitions(Transitions transitions) { - return Transitions.asRemoteTransitions(transitions); + static TaskViewFactoryController provideTaskViewFactoryController( + ShellTaskOrganizer shellTaskOrganizer, + @ShellMainThread ShellExecutor mainExecutor) { + return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor); } + // + // Misc + // + @WMSingleton @Provides - static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, - @ShellMainThread ShellExecutor mainExecutor, - @ShellAnimationThread ShellExecutor animExecutor) { - return new Transitions(organizer, pool, mainExecutor, animExecutor); + static ShellInit provideShellInit(DisplayImeController displayImeController, + DragAndDropController dragAndDropController, + ShellTaskOrganizer shellTaskOrganizer, + Optional<LegacySplitScreenController> legacySplitScreenOptional, + Optional<SplitScreenController> splitScreenOptional, + Optional<AppPairsController> appPairsOptional, + FullscreenTaskListener fullscreenTaskListener, + Transitions transitions, + @ShellMainThread ShellExecutor mainExecutor) { + return ShellInitImpl.create(displayImeController, + dragAndDropController, + shellTaskOrganizer, + legacySplitScreenOptional, + splitScreenOptional, + appPairsOptional, + fullscreenTaskListener, + transitions, + mainExecutor); } + /** + * Note, this is only optional because we currently pass this to the SysUI component scope and + * for non-primary users, we may inject a null-optional for that dependency. + */ @WMSingleton @Provides - static SizeCompatUI provideSizeCompatUI(Context context, DisplayController displayController, - DisplayImeController imeController, @ShellMainThread ShellExecutor mainExecutor) { - return SizeCompatUIController.create(context, displayController, imeController, - mainExecutor); + static Optional<ShellCommandHandler> provideShellCommandHandler( + ShellTaskOrganizer shellTaskOrganizer, + Optional<LegacySplitScreenController> legacySplitScreenOptional, + Optional<SplitScreenController> splitScreenOptional, + Optional<Pip> pipOptional, + Optional<OneHandedController> oneHandedOptional, + Optional<HideDisplayCutoutController> hideDisplayCutout, + Optional<AppPairsController> appPairsOptional, + @ShellMainThread ShellExecutor mainExecutor) { + return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer, + legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional, + hideDisplayCutout, appPairsOptional, mainExecutor)); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 2aaa0951d9d9..997b488a627f 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -21,10 +21,10 @@ import android.content.Context; import android.os.Handler; import android.view.IWindowManager; +import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -36,7 +36,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ChoreographerSfVsync; import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; @@ -60,11 +59,20 @@ import dagger.Module; import dagger.Provides; /** - * Provides dependencies from {@link com.android.wm.shell} which could be customized among different - * branches of SystemUI. + * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only + * accessible from components within the WM subcomponent (can be explicitly exposed to the + * SysUIComponent, see {@link WMComponent}). + * + * This module only defines Shell dependencies for handheld SystemUI implementation. Common + * dependencies should go into {@link WMShellBaseModule}. */ @Module(includes = WMShellBaseModule.class) public class WMShellModule { + + // + // Internal common - Components used internally by multiple shell features + // + @WMSingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, @@ -74,29 +82,37 @@ public class WMShellModule { transactionPool); } + // + // Split/multiwindow + // + @WMSingleton @Provides - static LegacySplitScreen provideLegacySplitScreen(Context context, + static LegacySplitScreenController provideLegacySplitScreen(Context context, DisplayController displayController, SystemWindows systemWindows, DisplayImeController displayImeController, TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener, Transitions transitions, @ShellMainThread ShellExecutor mainExecutor, @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) { - return LegacySplitScreenController.create(context, displayController, systemWindows, + return new LegacySplitScreenController(context, displayController, systemWindows, displayImeController, transactionPool, shellTaskOrganizer, syncQueue, taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler); } @WMSingleton @Provides - static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer, + static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) { - return AppPairsController.create(shellTaskOrganizer, syncQueue, displayController, + return new AppPairsController(shellTaskOrganizer, syncQueue, displayController, mainExecutor); } + // + // Pip + // + @WMSingleton @Provides static Optional<Pip> providePip(Context context, DisplayController displayController, @@ -161,7 +177,8 @@ public class WMShellModule { PipAnimationController pipAnimationController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, PipTransitionController pipTransitionController, - Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController, + Optional<LegacySplitScreenController> splitScreenOptional, + DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java index edaff5ff0f10..6c99efcc7128 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java @@ -32,13 +32,16 @@ import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManager; +import android.content.om.OverlayManagerTransaction; import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -87,6 +90,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { OverlayManager mOverlayManager; @Mock DumpManager mDumpManager; + @Mock + OverlayManagerTransaction.Builder mTransactionBuilder; private ThemeOverlayApplier mManager; @@ -94,7 +99,12 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(), - LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager); + LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) { + @Override + protected OverlayManagerTransaction.Builder getTransactionBuilder() { + return mTransactionBuilder; + } + }; when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM)) .thenReturn(Lists.newArrayList( createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR, @@ -148,9 +158,11 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void allCategoriesSpecified_allEnabledExclusively() { mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, TEST_USER_HANDLES); + verify(mOverlayManager).commit(any()); for (String overlayPackage : ALL_CATEGORIES_MAP.values()) { - verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER); + verify(mTransactionBuilder).setEnabled(eq(new OverlayIdentifier(overlayPackage)), + eq(true), eq(TEST_USER.getIdentifier())); } } @@ -160,11 +172,12 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { for (Map.Entry<String, String> entry : ALL_CATEGORIES_MAP.entrySet()) { if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) { - verify(mOverlayManager).setEnabledExclusiveInCategory( - entry.getValue(), UserHandle.SYSTEM); + verify(mTransactionBuilder).setEnabled(eq(new OverlayIdentifier(entry.getValue())), + eq(true), eq(UserHandle.SYSTEM.getIdentifier())); } else { - verify(mOverlayManager, never()).setEnabledExclusiveInCategory( - entry.getValue(), UserHandle.SYSTEM); + verify(mTransactionBuilder, never()).setEnabled( + eq(new OverlayIdentifier(entry.getValue())), + eq(true), eq(UserHandle.SYSTEM.getIdentifier())); } } } @@ -177,8 +190,10 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, userHandles); for (String overlayPackage : ALL_CATEGORIES_MAP.values()) { - verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER); - verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, newUserHandle); + verify(mTransactionBuilder).setEnabled(eq(new OverlayIdentifier(overlayPackage)), + eq(true), eq(TEST_USER.getIdentifier())); + verify(mTransactionBuilder).setEnabled(eq(new OverlayIdentifier(overlayPackage)), + eq(true), eq(newUserHandle.getIdentifier())); } } @@ -199,12 +214,15 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES); for (String overlayPackage : categoryToPackage.values()) { - verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER); + verify(mTransactionBuilder).setEnabled(eq(new OverlayIdentifier(overlayPackage)), + eq(true), eq(TEST_USER.getIdentifier())); } - verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS, - false, TEST_USER); - verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID, - false, TEST_USER); + verify(mTransactionBuilder).setEnabled( + eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS)), + eq(false), eq(TEST_USER.getIdentifier())); + verify(mTransactionBuilder).setEnabled( + eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID)), + eq(false), eq(TEST_USER.getIdentifier())); } @Test @@ -212,7 +230,9 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { mManager.applyCurrentUserOverlays(Maps.newArrayMap(), TEST_USER_HANDLES); for (String category : THEME_CATEGORIES) { - verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + category, false, TEST_USER); + verify(mTransactionBuilder).setEnabled( + eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + category)), eq(false), + eq(TEST_USER.getIdentifier())); } } @@ -223,9 +243,12 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES); - verify(mOverlayManager, never()).setEnabled("com.example.blah.category", false, TEST_USER); - verify(mOverlayManager, never()).setEnabledExclusiveInCategory("com.example.blah.category", - TEST_USER); + verify(mTransactionBuilder, never()).setEnabled( + eq(new OverlayIdentifier("com.example.blah.category")), eq(false), + eq(TEST_USER.getIdentifier())); + verify(mTransactionBuilder, never()).setEnabled( + eq(new OverlayIdentifier("com.example.blah.category")), eq(true), + eq(TEST_USER.getIdentifier())); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index c743fd07c492..365c62cddbdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -57,8 +57,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.statusbar.FeatureFlags; import org.junit.Before; import org.junit.Test; @@ -88,7 +87,6 @@ public class ToastUITest extends SysuiTestCase { private static final String TEXT = "Hello World"; private static final int MESSAGE_RES_ID = R.id.message; - private FakeExecutor mFakeDelayableExecutor = new FakeExecutor(new FakeSystemClock()); private Context mContextSpy; private ToastUI mToastUI; @Mock private LayoutInflater mLayoutInflater; @@ -99,6 +97,7 @@ public class ToastUITest extends SysuiTestCase { @Mock private PluginManager mPluginManager; @Mock private DumpManager mDumpManager; @Mock private ToastLogger mToastLogger; + @Mock private FeatureFlags mFeatureFlags; @Mock private ITransientNotificationCallback mCallback; @Captor private ArgumentCaptor<View> mViewCaptor; @@ -107,12 +106,9 @@ public class ToastUITest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - - // This is because inflate will result in WindowManager (WM) calls, which will fail since we - // are mocking it, so we mock LayoutInflater with the view obtained before mocking WM. - View view = ToastPresenter.getTextToastView(mContext, TEXT); - when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn(view); - mContext.addMockSystemService(LayoutInflater.class, mLayoutInflater); + when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn( + ToastPresenter.getTextToastView(mContext, TEXT)); + when(mFeatureFlags.isToastStyleEnabled()).thenReturn(false); mContext.addMockSystemService(WindowManager.class, mWindowManager); mContextSpy = spy(mContext); @@ -120,8 +116,8 @@ public class ToastUITest extends SysuiTestCase { doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt()); mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager, - mAccessibilityManager, new ToastFactory(mPluginManager, mDumpManager), - mFakeDelayableExecutor, mToastLogger); + mAccessibilityManager, new ToastFactory(mLayoutInflater, mPluginManager, + mDumpManager, mFeatureFlags), mToastLogger); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 76269dda8245..f1fc0b7723fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -89,8 +89,6 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.Bubble; @@ -297,7 +295,7 @@ public class BubblesTest extends SysuiTestCase { mBubblesManager = new BubblesManager( mContext, - mBubbleController.getImpl(), + mBubbleController.asBubbles(), mNotificationShadeWindowController, mStatusBarStateController, mShadeController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index 5340ff7e967c..9e10b21ce3b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -261,7 +261,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mBubblesManager = new BubblesManager( mContext, - mBubbleController.getImpl(), + mBubbleController.asBubbles(), mNotificationShadeWindowController, mStatusBarStateController, mShadeController, diff --git a/services/core/Android.bp b/services/core/Android.bp index 83a5036f83a9..25890b056c33 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -82,6 +82,7 @@ java_library_static { ":framework_native_aidl", ":gsiservice_aidl", ":idmap2_aidl", + ":idmap2_core_aidl", ":inputconstants_aidl", ":installd_aidl", ":storaged_aidl", diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 737a9e4ff46c..342208c8a32b 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -998,6 +998,18 @@ public abstract class PackageManagerInternal { public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId); /** + * Register to listen for loading progress of an installed package. + * The listener is automatically unregistered when the app is fully loaded. + * @param packageName The name of the installed package + * @param callback To loading reporting progress + * @param userId The user under which to check. + * @return Whether the registration was successful. It can fail if the package has not been + * installed yet. + */ + public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName, + @NonNull InstalledLoadingProgressCallback callback, int userId); + + /** * Returns the string representation of a known package. For example, * {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard. * diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 958c15c8d432..e996eb4ef674 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -17,6 +17,7 @@ package android.os; import com.android.internal.os.BinderCallsStats; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import java.util.Collection; @@ -37,6 +38,9 @@ public abstract class BatteryStatsInternal { */ public abstract String[] getMobileIfaces(); + /** Returns CPU times for system server thread groups. */ + public abstract SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes(); + /** * Inform battery stats how many deferred jobs existed when the app got launched and how * long ago was the last job execution for the app. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 42b6a7f1aa87..277152d82c34 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -6407,20 +6407,18 @@ public class ConnectivityService extends IConnectivityManager.Stub Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis())); } - // Prioritize the user portal URL from the network agent. - if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null - || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) { - captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl()); - } - // Prioritize the venue information URL from the network agent. - if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null - || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) { - captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl()); - - // Note that venue friendly name can only come from the network agent because it is not - // in use in RFC8908. However, if using the Capport venue URL, make sure that the - // friendly name is not set from the network agent. - captivePortalBuilder.setVenueFriendlyName(null); + // Prioritize the user portal URL from the network agent if the source is authenticated. + if (apiData.getUserPortalUrl() != null && naData.getUserPortalUrlSource() + != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) { + captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl(), + apiData.getUserPortalUrlSource()); + } + // Prioritize the venue information URL from the network agent if the source is + // authenticated. + if (apiData.getVenueInfoUrl() != null && naData.getVenueInfoUrlSource() + != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) { + captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl(), + apiData.getVenueInfoUrlSource()); } return captivePortalBuilder.build(); } diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index 00d8b0f1bed4..d10cf4dd0505 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.service.persistentdata.IPersistentDataBlockService; import android.service.persistentdata.PersistentDataBlockManager; +import android.text.TextUtils; import android.util.Slog; import com.android.internal.R; @@ -147,14 +148,15 @@ public class PersistentDataBlockService extends SystemService { private int getAllowedUid(int userHandle) { String allowedPackage = mContext.getResources() .getString(R.string.config_persistentDataPackageName); - PackageManager pm = mContext.getPackageManager(); int allowedUid = -1; - try { - allowedUid = pm.getPackageUidAsUser(allowedPackage, - PackageManager.MATCH_SYSTEM_ONLY, userHandle); - } catch (PackageManager.NameNotFoundException e) { - // not expected - Slog.e(TAG, "not able to find package " + allowedPackage, e); + if (!TextUtils.isEmpty(allowedPackage)) { + try { + allowedUid = mContext.getPackageManager().getPackageUidAsUser( + allowedPackage, PackageManager.MATCH_SYSTEM_ONLY, userHandle); + } catch (PackageManager.NameNotFoundException e) { + // not expected + Slog.e(TAG, "not able to find package " + allowedPackage, e); + } } return allowedUid; } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 336a59576d8d..6500f6d1b6d3 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -72,6 +72,7 @@ import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ParseUtils; @@ -357,6 +358,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override + public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() { + return mStats.getSystemServiceCpuThreadTimes(); + } + + @Override public void noteJobsDeferred(int uid, int numDeferred, long sinceLast) { if (DBG) Slog.d(TAG, "Jobs deferred " + uid + ": " + numDeferred + " " + sinceLast); BatteryStatsService.this.noteJobsDeferred(uid, numDeferred, sinceLast); diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 7e65434b8189..8b6fabd7faff 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -967,6 +967,12 @@ public final class BroadcastQueue { } } + static String broadcastDescription(BroadcastRecord r, ComponentName component) { + return r.intent.toString() + + " from " + r.callerPackage + " (pid=" + r.callingPid + + ", uid=" + r.callingUid + ") to " + component.flattenToShortString(); + } + final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { BroadcastRecord r; @@ -1349,14 +1355,18 @@ public final class BroadcastQueue { < brOptions.getMinManifestReceiverApiLevel() || info.activityInfo.applicationInfo.targetSdkVersion > brOptions.getMaxManifestReceiverApiLevel())) { + Slog.w(TAG, "Target SDK mismatch: receiver " + info.activityInfo + + " targets " + info.activityInfo.applicationInfo.targetSdkVersion + + " but delivery restricted to [" + + brOptions.getMinManifestReceiverApiLevel() + ", " + + brOptions.getMaxManifestReceiverApiLevel() + + "] broadcasting " + broadcastDescription(r, component)); skip = true; } if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid, component.getPackageName(), info.activityInfo.applicationInfo.uid)) { Slog.w(TAG, "Association not allowed: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ") to " + component.flattenToShortString()); + + broadcastDescription(r, component)); skip = true; } if (!skip) { @@ -1364,9 +1374,7 @@ public final class BroadcastQueue { r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid); if (skip) { Slog.w(TAG, "Firewall blocked: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ") to " + component.flattenToShortString()); + + broadcastDescription(r, component)); } } int perm = mService.checkComponentPermission(info.activityInfo.permission, @@ -1375,18 +1383,12 @@ public final class BroadcastQueue { if (!skip && perm != PackageManager.PERMISSION_GRANTED) { if (!info.activityInfo.exported) { Slog.w(TAG, "Permission Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ")" - + " is not exported from uid " + info.activityInfo.applicationInfo.uid - + " due to receiver " + component.flattenToShortString()); + + broadcastDescription(r, component) + + " is not exported from uid " + info.activityInfo.applicationInfo.uid); } else { Slog.w(TAG, "Permission Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ")" - + " requires " + info.activityInfo.permission - + " due to receiver " + component.flattenToShortString()); + + broadcastDescription(r, component) + + " requires " + info.activityInfo.permission); } skip = true; } else if (!skip && info.activityInfo.permission != null) { @@ -1396,13 +1398,9 @@ public final class BroadcastQueue { "Broadcast delivered to " + info.activityInfo.name) != AppOpsManager.MODE_ALLOWED) { Slog.w(TAG, "Appop Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" - + r.callingPid + ", uid=" + r.callingUid + ")" + + broadcastDescription(r, component) + " requires appop " + AppOpsManager.permissionToOp( - info.activityInfo.permission) - + " due to registered receiver " - + component.flattenToShortString()); + info.activityInfo.permission)); skip = true; } } @@ -1520,7 +1518,7 @@ public final class BroadcastQueue { + info.activityInfo.packageName, e); } if (!isAvailable) { - if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, + Slog.w(TAG_BROADCAST, "Skipping delivery to " + info.activityInfo.packageName + " / " + info.activityInfo.applicationInfo.uid + " : package no longer available"); @@ -1536,6 +1534,9 @@ public final class BroadcastQueue { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, info.activityInfo.packageName, UserHandle.getUserId( info.activityInfo.applicationInfo.uid))) { + Slog.w(TAG_BROADCAST, + "Skipping delivery: permission review required for " + + broadcastDescription(r, component)); skip = true; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index baeb3fdda67d..81e90df5802b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -496,6 +496,7 @@ public class Sensor { } void setTestHalEnabled(boolean enabled) { + Slog.w(mTag, "setTestHalEnabled: " + enabled); mTestHalEnabled = enabled; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java index 1d57073d6516..a38da3ad70b3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java @@ -23,86 +23,85 @@ import android.hardware.biometrics.face.ISessionCallback; import android.hardware.biometrics.face.SensorProps; import android.hardware.common.NativeHandle; import android.hardware.keymaster.HardwareAuthToken; -import android.os.Binder; -import android.os.IBinder; +import android.util.Slog; /** * Test HAL that provides only no-ops. */ public class TestHal extends IFace.Stub { + private static final String TAG = "face.aidl.TestHal"; @Override public SensorProps[] getSensorProps() { + Slog.w(TAG, "getSensorProps"); return new SensorProps[0]; } @Override public ISession createSession(int sensorId, int userId, ISessionCallback cb) { - return new ISession() { + return new ISession.Stub() { @Override public void generateChallenge(int cookie, int timeoutSec) { - + Slog.w(TAG, "generateChallenge, cookie: " + cookie); } @Override public void revokeChallenge(int cookie, long challenge) { - + Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie); } @Override public ICancellationSignal enroll(int cookie, HardwareAuthToken hat, byte enrollmentType, byte[] features, NativeHandle previewSurface) { + Slog.w(TAG, "enroll, cookie: " + cookie); return null; } @Override public ICancellationSignal authenticate(int cookie, long operationId) { + Slog.w(TAG, "authenticate, cookie: " + cookie); return null; } @Override public ICancellationSignal detectInteraction(int cookie) { + Slog.w(TAG, "detectInteraction, cookie: " + cookie); return null; } @Override public void enumerateEnrollments(int cookie) { - + Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie); } @Override public void removeEnrollments(int cookie, int[] enrollmentIds) { - + Slog.w(TAG, "removeEnrollments, cookie: " + cookie); } @Override public void getFeatures(int cookie, int enrollmentId) { - + Slog.w(TAG, "getFeatures, cookie: " + cookie); } @Override public void setFeature(int cookie, HardwareAuthToken hat, int enrollmentId, byte feature, boolean enabled) { - + Slog.w(TAG, "setFeature, cookie: " + cookie); } @Override public void getAuthenticatorId(int cookie) { - + Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie); } @Override public void invalidateAuthenticatorId(int cookie) { - + Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie); } @Override public void resetLockout(int cookie, HardwareAuthToken hat) { - - } - - @Override - public IBinder asBinder() { - return new Binder(); + Slog.w(TAG, "resetLockout, cookie: " + cookie); } }; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java index bab1114dc70d..00ca8025564d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java @@ -25,10 +25,12 @@ import android.hardware.biometrics.face.V1_0.Status; import android.hardware.biometrics.face.V1_1.IBiometricsFace; import android.os.NativeHandle; import android.os.RemoteException; +import android.util.Slog; import java.util.ArrayList; public class TestHal extends IBiometricsFace.Stub { + private static final String TAG = "face.hidl.TestHal"; @Nullable private IBiometricsFaceClientCallback mCallback; @@ -47,6 +49,7 @@ public class TestHal extends IBiometricsFace.Stub { @Override public OptionalUint64 generateChallenge(int challengeTimeoutSec) { + Slog.w(TAG, "generateChallenge"); final OptionalUint64 result = new OptionalUint64(); result.status = Status.OK; result.value = 0; @@ -55,6 +58,7 @@ public class TestHal extends IBiometricsFace.Stub { @Override public int enroll(ArrayList<Byte> hat, int timeoutSec, ArrayList<Integer> disabledFeatures) { + Slog.w(TAG, "enroll"); return 0; } @@ -95,16 +99,19 @@ public class TestHal extends IBiometricsFace.Stub { @Override public int enumerate() { + Slog.w(TAG, "enumerate"); return 0; } @Override public int remove(int faceId) { + Slog.w(TAG, "remove"); return 0; } @Override public int authenticate(long operationId) { + Slog.w(TAG, "authenticate"); return 0; } @@ -115,6 +122,7 @@ public class TestHal extends IBiometricsFace.Stub { @Override public int resetLockout(ArrayList<Byte> hat) { + Slog.w(TAG, "resetLockout"); return 0; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index 7e4ee9e77ab2..73b59cfdf248 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -476,6 +476,7 @@ class Sensor { } void setTestHalEnabled(boolean enabled) { + Slog.w(mTag, "setTestHalEnabled, enabled"); mTestHalEnabled = enabled; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java index a31bcdcaf4e6..66b68eeb335b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java @@ -22,89 +22,89 @@ import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.ISessionCallback; import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.keymaster.HardwareAuthToken; -import android.os.Binder; -import android.os.IBinder; +import android.util.Slog; /** * Test HAL that provides only provides no-ops. */ public class TestHal extends IFingerprint.Stub { + private static final String TAG = "fingerprint.aidl.TestHal"; + @Override public SensorProps[] getSensorProps() { + Slog.w(TAG, "getSensorProps"); return new SensorProps[0]; } @Override public ISession createSession(int sensorId, int userId, ISessionCallback cb) { - return new ISession() { + return new ISession.Stub() { @Override public void generateChallenge(int cookie, int timeoutSec) { - + Slog.w(TAG, "generateChallenge, cookie: " + cookie); } @Override public void revokeChallenge(int cookie, long challenge) { - + Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie); } @Override public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) { + Slog.w(TAG, "enroll, cookie: " + cookie); return null; } @Override public ICancellationSignal authenticate(int cookie, long operationId) { + Slog.w(TAG, "authenticate, cookie: " + cookie); return null; } @Override public ICancellationSignal detectInteraction(int cookie) { + Slog.w(TAG, "detectInteraction, cookie: " + cookie); return null; } @Override public void enumerateEnrollments(int cookie) { - + Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie); } @Override public void removeEnrollments(int cookie, int[] enrollmentIds) { - + Slog.w(TAG, "removeEnrollments, cookie: " + cookie); } @Override public void getAuthenticatorId(int cookie) { - + Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie); } @Override public void invalidateAuthenticatorId(int cookie) { - + Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie); } @Override public void resetLockout(int cookie, HardwareAuthToken hat) { - + Slog.w(TAG, "resetLockout, cookie: " + cookie); } @Override public void onPointerDown(int pointerId, int x, int y, float minor, float major) { - + Slog.w(TAG, "onPointerDown"); } @Override public void onPointerUp(int pointerId) { - + Slog.w(TAG, "onPointerUp"); } @Override public void onUiReady() { - - } - - @Override - public IBinder asBinder() { - return new Binder(); + Slog.w(TAG, "onUiReady"); } }; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java index b7aec0ed2731..57447f3a8cf7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java @@ -21,11 +21,14 @@ import android.hardware.biometrics.fingerprint.V2_1.FingerprintError; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback; import android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint; import android.os.RemoteException; +import android.util.Slog; /** * Test HAL that provides only provides no-ops. */ public class TestHal extends IBiometricsFingerprint.Stub { + private static final String TAG = "fingerprint.hidl.TestHal"; + @Nullable private IBiometricsFingerprintClientCallback mCallback; @@ -57,6 +60,7 @@ public class TestHal extends IBiometricsFingerprint.Stub { @Override public int enroll(byte[] hat, int gid, int timeoutSec) { + Slog.w(TAG, "enroll"); return 0; } @@ -80,11 +84,13 @@ public class TestHal extends IBiometricsFingerprint.Stub { @Override public int enumerate() { + Slog.w(TAG, "Enumerate"); return 0; } @Override public int remove(int gid, int fid) { + Slog.w(TAG, "Remove"); return 0; } @@ -95,6 +101,7 @@ public class TestHal extends IBiometricsFingerprint.Stub { @Override public int authenticate(long operationId, int gid) { + Slog.w(TAG, "Authenticate"); return 0; } }
\ No newline at end of file diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 3d71b0a269c9..6f112d73ba14 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -161,13 +161,20 @@ public class NetworkNotificationManager { if (nai != null) { transportType = approximateTransportType(nai); final String extraInfo = nai.networkInfo.getExtraInfo(); - name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo; + if (nai.linkProperties != null && nai.linkProperties.getCaptivePortalData() != null + && !TextUtils.isEmpty(nai.linkProperties.getCaptivePortalData() + .getVenueFriendlyName())) { + name = nai.linkProperties.getCaptivePortalData().getVenueFriendlyName(); + } else { + name = TextUtils.isEmpty(extraInfo) + ? WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()) : extraInfo; + } // Only notify for Internet-capable networks. if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return; } else { // Legacy notifications. transportType = TRANSPORT_CELLULAR; - name = null; + name = ""; } // Clear any previous notification with lower priority, otherwise return. http://b/63676954. @@ -193,35 +200,30 @@ public class NetworkNotificationManager { final CharSequence details; int icon = getIcon(transportType); if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.wifi_no_internet, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.wifi_no_internet, name); details = r.getString(R.string.wifi_no_internet_detailed); } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) { if (transportType == TRANSPORT_CELLULAR) { title = r.getString(R.string.mobile_no_internet); } else if (transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.wifi_no_internet, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.wifi_no_internet, name); } else { title = r.getString(R.string.other_networks_no_internet); } details = r.getString(R.string.private_dns_broken_detailed); } else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY && transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.network_partial_connectivity, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.network_partial_connectivity, name); details = r.getString(R.string.network_partial_connectivity_detailed); } else if (notifyType == NotificationType.LOST_INTERNET && transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.wifi_no_internet, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.wifi_no_internet, name); details = r.getString(R.string.wifi_no_internet_detailed); } else if (notifyType == NotificationType.SIGN_IN) { switch (transportType) { case TRANSPORT_WIFI: title = r.getString(R.string.wifi_available_sign_in, 0); - details = r.getString(R.string.network_available_sign_in_detailed, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + details = r.getString(R.string.network_available_sign_in_detailed, name); break; case TRANSPORT_CELLULAR: title = r.getString(R.string.network_available_sign_in, 0); diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java index b5f20d70db7f..c480594b8c60 100644 --- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java +++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java @@ -41,7 +41,6 @@ import android.os.Handler; import android.os.MessageQueue; import android.os.Messenger; import android.system.ErrnoException; -import android.system.Int32Ref; import android.system.Os; import android.util.Log; import android.util.SparseArray; @@ -306,9 +305,8 @@ public class TcpKeepaliveController { private static boolean isReceiveQueueEmpty(FileDescriptor fd) throws ErrnoException { - Int32Ref result = new Int32Ref(-1); - Os.ioctlInt(fd, SIOCINQ, result); - if (result.value != 0) { + final int result = Os.ioctlInt(fd, SIOCINQ); + if (result != 0) { Log.e(TAG, "Read queue has data"); return false; } @@ -317,9 +315,8 @@ public class TcpKeepaliveController { private static boolean isSendQueueEmpty(FileDescriptor fd) throws ErrnoException { - Int32Ref result = new Int32Ref(-1); - Os.ioctlInt(fd, SIOCOUTQ, result); - if (result.value != 0) { + final int result = Os.ioctlInt(fd, SIOCOUTQ); + if (result != 0) { Log.e(TAG, "Write queue has data"); return false; } diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java index 017f11ceaf06..d514aab31e8d 100644 --- a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java +++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java @@ -17,6 +17,8 @@ package com.android.server.graphics.fonts; import android.annotation.NonNull; +import android.graphics.FontListParser; +import android.text.FontConfig; import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; @@ -30,6 +32,8 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; import java.util.Set; /* package */ class PersistentSystemFontConfig { @@ -38,11 +42,13 @@ import java.util.Set; private static final String TAG_ROOT = "fontConfig"; private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate"; private static final String TAG_UPDATED_FONT_DIR = "updatedFontDir"; + private static final String TAG_FAMILY = "family"; private static final String ATTR_VALUE = "value"; /* package */ static class Config { public long lastModifiedDate; public final Set<String> updatedFontDirs = new ArraySet<>(); + public final List<FontConfig.FontFamily> fontFamilies = new ArrayList<>(); } /** @@ -72,6 +78,11 @@ import java.util.Set; case TAG_UPDATED_FONT_DIR: out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE)); break; + case TAG_FAMILY: + // updatableFontMap is not ready here. We get the base file names by passing + // empty fontDir, and resolve font paths later. + out.fontFamilies.add(FontListParser.readFamily( + parser, "" /* fontDir */, null /* updatableFontMap */)); default: Slog.w(TAG, "Skipping unknown tag: " + tag); } @@ -97,6 +108,13 @@ import java.util.Set; out.attribute(null, ATTR_VALUE, dir); out.endTag(null, TAG_UPDATED_FONT_DIR); } + List<FontConfig.FontFamily> fontFamilies = config.fontFamilies; + for (int i = 0; i < fontFamilies.size(); i++) { + FontConfig.FontFamily fontFamily = fontFamilies.get(i); + out.startTag(null, TAG_FAMILY); + FontListParser.writeFamily(out, fontFamily); + out.endTag(null, TAG_FAMILY); + } out.endTag(null, TAG_ROOT); out.endDocument(); diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index dac94f6aa9d2..45f2a38b6773 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -31,6 +31,8 @@ import android.util.ArrayMap; import android.util.Base64; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; + import org.xmlpull.v1.XmlPullParserException; import java.io.File; @@ -40,6 +42,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.security.SecureRandom; import java.time.Instant; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -118,6 +121,12 @@ final class UpdatableFontDir { */ private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>(); + /** + * A mutable map containing mapping from font family name to {@link FontConfig.FontFamily}. + * The FontFamily entries only reference font files in {@link #mFontFileInfoMap}. + */ + private final ArrayMap<String, FontConfig.FontFamily> mFontFamilyMap = new ArrayMap<>(); + UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser, FsverityUtil fsverityUtil) { this(filesDir, preinstalledFontDirs, parser, fsverityUtil, new File(CONFIG_XML_FILE)); @@ -136,6 +145,7 @@ final class UpdatableFontDir { /* package */ void loadFontFileMap() { mFontFileInfoMap.clear(); + mFontFamilyMap.clear(); mLastModifiedDate = 0; boolean success = false; try { @@ -168,6 +178,13 @@ final class UpdatableFontDir { FontFileInfo fontFileInfo = validateFontFile(files[0]); addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */); } + // Resolve font file paths. + List<FontConfig.FontFamily> fontFamilies = config.fontFamilies; + for (int i = 0; i < fontFamilies.size(); i++) { + FontConfig.FontFamily fontFamily = fontFamilies.get(i); + // Ignore failures as updated fonts may be obsoleted by system OTA update. + addFontFamily(fontFamily); + } success = true; } catch (Throwable t) { // If something happened during loading system fonts, clear all contents in finally @@ -177,6 +194,7 @@ final class UpdatableFontDir { // Delete all files just in case if we find a problematic file. if (!success) { mFontFileInfoMap.clear(); + mFontFamilyMap.clear(); mLastModifiedDate = 0; FileUtils.deleteContents(mFilesDir); } @@ -186,10 +204,11 @@ final class UpdatableFontDir { /* package */ void clearUpdates() throws SystemFontException { mFontFileInfoMap.clear(); FileUtils.deleteContents(mFilesDir); + mFontFamilyMap.clear(); mLastModifiedDate = Instant.now().getEpochSecond(); try (FileOutputStream fos = new FileOutputStream(mConfigFile)) { - PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig()); + PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig()); } catch (Exception e) { throw new SystemFontException( FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG, @@ -206,17 +225,29 @@ final class UpdatableFontDir { public void update(List<FontUpdateRequest> requests) throws SystemFontException { // Backup the mapping for rollback. ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap); + ArrayMap<String, FontConfig.FontFamily> backupFamilies = new ArrayMap<>(mFontFamilyMap); long backupLastModifiedDate = mLastModifiedDate; boolean success = false; try { for (FontUpdateRequest request : requests) { - installFontFile(request.getFd().getFileDescriptor(), request.getSignature()); + switch (request.getType()) { + case FontUpdateRequest.TYPE_UPDATE_FONT_FILE: + installFontFile( + request.getFd().getFileDescriptor(), request.getSignature()); + break; + case FontUpdateRequest.TYPE_UPDATE_FONT_FAMILY: + // TODO: define error code. + if (!addFontFamily(request.getFontFamily())) { + throw new IllegalArgumentException("Invalid font family"); + } + break; + } } // Write config file. mLastModifiedDate = Instant.now().getEpochSecond(); try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) { - PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig()); + PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig()); } catch (Exception e) { throw new SystemFontException( FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG, @@ -234,6 +265,8 @@ final class UpdatableFontDir { if (!success) { mFontFileInfoMap.clear(); mFontFileInfoMap.putAll(backupMap); + mFontFamilyMap.clear(); + mFontFamilyMap.putAll(backupFamilies); mLastModifiedDate = backupLastModifiedDate; } } @@ -454,12 +487,52 @@ final class UpdatableFontDir { } } - private PersistentSystemFontConfig.Config getPersistentConfig() { + /** + * Adds a font family to {@link #mFontFamilyMap} and returns true on success. + * + * <p>This method only accepts adding or updating a font family with a name. + * This is to prevent bad font family update from removing glyphs from font fallback chains. + * Unnamed font families are used as other named font family's fallback fonts to guarantee a + * complete glyph coverage. + */ + private boolean addFontFamily(FontConfig.FontFamily fontFamily) { + if (fontFamily.getName() == null) { + Slog.e(TAG, "Name is null."); + return false; + } + FontConfig.FontFamily resolvedFontFamily = resolveFontFiles(fontFamily); + if (resolvedFontFamily == null) { + Slog.e(TAG, "Required fonts are not available"); + return false; + } + mFontFamilyMap.put(resolvedFontFamily.getName(), resolvedFontFamily); + return true; + } + + @Nullable + private FontConfig.FontFamily resolveFontFiles(FontConfig.FontFamily fontFamily) { + List<FontConfig.Font> resolvedFonts = new ArrayList<>(fontFamily.getFontList().size()); + List<FontConfig.Font> fontList = fontFamily.getFontList(); + for (int i = 0; i < fontList.size(); i++) { + FontConfig.Font font = fontList.get(i); + FontFileInfo info = mFontFileInfoMap.get(font.getFile().getName()); + if (info == null) { + return null; + } + resolvedFonts.add(new FontConfig.Font(info.mFile, null, font.getStyle(), + font.getTtcIndex(), font.getFontVariationSettings(), font.getFontFamilyName())); + } + return new FontConfig.FontFamily(resolvedFonts, fontFamily.getName(), + fontFamily.getLocaleList(), fontFamily.getVariant()); + } + + private PersistentSystemFontConfig.Config createPersistentConfig() { PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); config.lastModifiedDate = mLastModifiedDate; for (FontFileInfo info : mFontFileInfoMap.values()) { config.updatedFontDirs.add(info.getRandomizedFontDir().getName()); } + config.fontFamilies.addAll(mFontFamilyMap.values()); return config; } @@ -471,8 +544,24 @@ final class UpdatableFontDir { return map; } + @VisibleForTesting + Map<String, FontConfig.FontFamily> getFontFamilyMap() { + return mFontFamilyMap; + } + /* package */ FontConfig getSystemFontConfig() { - return SystemFonts.getSystemFontConfig(getFontFileMap(), mLastModifiedDate, mConfigVersion); + FontConfig config = SystemFonts.getSystemFontConfig(getFontFileMap(), 0, 0); + List<FontConfig.FontFamily> mergedFamilies = + new ArrayList<>(config.getFontFamilies().size() + mFontFamilyMap.size()); + // We should keep the first font family (config.getFontFamilies().get(0)) because it's used + // as a fallback font. See SystemFonts.java. + mergedFamilies.addAll(config.getFontFamilies()); + // When building Typeface, a latter font family definition will override the previous font + // family definition with the same name. An exception is config.getFontFamilies.get(0), + // which will be used as a fallback font without being overridden. + mergedFamilies.addAll(mFontFamilyMap.values()); + return new FontConfig( + mergedFamilies, config.getAliases(), mLastModifiedDate, mConfigVersion); } /* package */ int getConfigVersion() { diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java index 1a0a639c3c66..7b646b36124b 100644 --- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java +++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; */ import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback; import android.hardware.hdmi.IHdmiControlCallback; @@ -65,6 +66,20 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { @Override boolean start() { + HdmiControlService service = localDevice().mService; + if (service.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) { + HdmiDeviceInfo deviceInfo = service.getHdmiCecNetwork().getCecDeviceInfo( + mTargetAddress); + if (deviceInfo != null + && deviceInfo.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) { + int powerStatus = deviceInfo.getDevicePowerStatus(); + if (powerStatus != HdmiControlManager.POWER_STATUS_UNKNOWN) { + invokeCallback(powerStatus); + finish(); + return true; + } + } + } queryDevicePowerStatus(); mState = STATE_WAITING_FOR_REPORT_POWER_STATUS; addTimer(mState, HdmiConfig.TIMEOUT_MS); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 75b52f95d5bb..299525207a60 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -239,6 +239,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void onActiveSourceLost() { assertRunOnServiceThread(); + mService.pauseActiveMediaSessions(); switch (mService.getHdmiCecConfig().getStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)) { case HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW: diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index bdf92ca4a7ef..fa1fb48ce124 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -56,6 +56,8 @@ import android.hardware.hdmi.IHdmiVendorCommandListener; import android.hardware.tv.cec.V1_0.OptionKey; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.media.AudioManager; +import android.media.session.MediaController; +import android.media.session.MediaSessionManager; import android.media.tv.TvInputManager; import android.media.tv.TvInputManager.TvInputCallback; import android.net.Uri; @@ -3294,6 +3296,16 @@ public class HdmiControlService extends SystemService { } } + @VisibleForTesting + void pauseActiveMediaSessions() { + MediaSessionManager mediaSessionManager = getContext() + .getSystemService(MediaSessionManager.class); + List<MediaController> mediaControllers = mediaSessionManager.getActiveSessions(null); + for (MediaController mediaController : mediaControllers) { + mediaController.getTransportControls().pause(); + } + } + void setActiveSource(int logicalAddress, int physicalAddress, String caller) { synchronized (mLock) { mActiveSource.logicalAddress = logicalAddress; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1a4c8b7d6571..00ae30e2aa0e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5246,12 +5246,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub imeTracing.startTrace(null); }); } - } - doDump(fd, pw, args, asProto); - } - private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) { - if (useProto) { final ProtoOutputStream proto = new ProtoOutputStream(fd); dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE); proto.flush(); diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 28dc5167487d..47cb43e2d4af 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -1253,7 +1253,8 @@ public class LocationManagerService extends ILocationManager.Stub { ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats = mEventLog.copyAggregateStats(); for (int i = 0; i < aggregateStats.size(); i++) { - ipw.println(aggregateStats.keyAt(i)); + ipw.print(aggregateStats.keyAt(i)); + ipw.println(":"); ipw.increaseIndent(); ArrayMap<String, LocationEventLog.AggregateStats> providerStats = aggregateStats.valueAt(i); diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java index 67060fc2c082..865d41f1baee 100644 --- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java +++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java @@ -164,6 +164,7 @@ public class LocationEventLog extends LocalEventLog { if (Build.IS_DEBUGGABLE || D) { addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity); } + getAggregateStats(provider, identity.getPackageName()).markLocationDelivered(); } /** Logs that the location power save mode has changed. */ @@ -397,6 +398,8 @@ public class LocationEventLog extends LocalEventLog { private int mActiveRequestCount; @GuardedBy("this") private int mForegroundRequestCount; + @GuardedBy("this") + private int mDeliveredLocationCount; @GuardedBy("this") private long mFastestIntervalMs = Long.MAX_VALUE; @@ -464,6 +467,10 @@ public class LocationEventLog extends LocalEventLog { Preconditions.checkState(mForegroundRequestCount >= 0); } + synchronized void markLocationDelivered() { + mDeliveredLocationCount++; + } + public synchronized void updateTotals() { if (mAddedRequestCount > 0) { long realtimeMs = SystemClock.elapsedRealtime(); @@ -488,7 +495,8 @@ public class LocationEventLog extends LocalEventLog { + intervalToString(mSlowestIntervalMs) + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs) + "/" + formatDuration(mActiveTimeTotalMs) + "/" - + formatDuration(mForegroundTimeTotalMs); + + formatDuration(mForegroundTimeTotalMs) + ", locations = " + + mDeliveredLocationCount; } private static String intervalToString(long intervalMs) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 28c90e965e47..cb9793f088c1 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2372,10 +2372,17 @@ public class LockSettingsService extends ILockSettings.Stub { public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { enforceShell(); + final int origPid = Binder.getCallingPid(); + final int origUid = Binder.getCallingUid(); + + // The original identity is an opaque integer. final long origId = Binder.clearCallingIdentity(); + Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid); try { - (new LockSettingsShellCommand(new LockPatternUtils(mContext))).exec( - this, in, out, err, args, callback, resultReceiver); + final LockSettingsShellCommand command = + new LockSettingsShellCommand(new LockPatternUtils(mContext), mContext, origPid, + origUid); + command.exec(this, in, out, err, args, callback, resultReceiver); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 834cf0536865..67fae05c64f4 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -21,8 +21,11 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTE import android.app.ActivityManager; import android.app.admin.PasswordMetrics; +import android.content.Context; import android.os.ShellCommand; +import android.os.SystemProperties; import android.text.TextUtils; +import android.util.Slog; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils.RequestThrottledException; @@ -43,15 +46,25 @@ class LockSettingsShellCommand extends ShellCommand { private static final String COMMAND_VERIFY = "verify"; private static final String COMMAND_GET_DISABLED = "get-disabled"; private static final String COMMAND_REMOVE_CACHE = "remove-cache"; + private static final String COMMAND_SET_ROR_PROVIDER_PACKAGE = + "set-resume-on-reboot-provider-package"; private static final String COMMAND_HELP = "help"; private int mCurrentUserId; private final LockPatternUtils mLockPatternUtils; + private final Context mContext; + private final int mCallingPid; + private final int mCallingUid; + private String mOld = ""; private String mNew = ""; - LockSettingsShellCommand(LockPatternUtils lockPatternUtils) { + LockSettingsShellCommand(LockPatternUtils lockPatternUtils, Context context, int callingPid, + int callingUid) { mLockPatternUtils = lockPatternUtils; + mCallingPid = callingPid; + mCallingUid = callingUid; + mContext = context; } @Override @@ -68,6 +81,7 @@ class LockSettingsShellCommand extends ShellCommand { case COMMAND_HELP: case COMMAND_GET_DISABLED: case COMMAND_SET_DISABLED: + case COMMAND_SET_ROR_PROVIDER_PACKAGE: break; default: getErrPrintWriter().println( @@ -80,6 +94,9 @@ class LockSettingsShellCommand extends ShellCommand { case COMMAND_REMOVE_CACHE: runRemoveCache(); return 0; + case COMMAND_SET_ROR_PROVIDER_PACKAGE: + runSetResumeOnRebootProviderPackage(); + return 0; case COMMAND_HELP: onHelp(); return 0; @@ -170,6 +187,9 @@ class LockSettingsShellCommand extends ShellCommand { pw.println(""); pw.println(" remove-cache [--user USER_ID]"); pw.println(" Removes cached unified challenge for the managed profile."); + pw.println(" set-resume-on-reboot-provider-package <package_name>"); + pw.println(" Sets the package name for server based resume on reboot service " + + "provider."); pw.println(""); } } @@ -256,6 +276,17 @@ class LockSettingsShellCommand extends ShellCommand { return true; } + private boolean runSetResumeOnRebootProviderPackage() { + final String packageName = mNew; + String name = ResumeOnRebootServiceProvider.PROP_ROR_PROVIDER_PACKAGE; + Slog.i(TAG, "Setting " + name + " to " + packageName); + + mContext.enforcePermission(android.Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE, + mCallingPid, mCallingUid, TAG); + SystemProperties.set(name, packageName); + return true; + } + private boolean runClear() { LockscreenCredential none = LockscreenCredential.createNone(); if (!isNewCredentialSufficient(none)) { diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java index a1e18bd5a6bd..9c471b85eb76 100644 --- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java +++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java @@ -31,6 +31,7 @@ import android.os.IBinder; import android.os.ParcelableException; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.service.resumeonreboot.IResumeOnRebootService; @@ -55,6 +56,10 @@ public class ResumeOnRebootServiceProvider { Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE; private static final String TAG = "ResumeOnRebootServiceProvider"; + // The system property name that overrides the default service provider package name. + static final String PROP_ROR_PROVIDER_PACKAGE = + "persist.sys.resume_on_reboot_provider_package"; + private final Context mContext; private final PackageManager mPackageManager; @@ -72,12 +77,19 @@ public class ResumeOnRebootServiceProvider { private ServiceInfo resolveService() { Intent intent = new Intent(); intent.setAction(ResumeOnRebootService.SERVICE_INTERFACE); - if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) { - intent.setPackage(PROVIDER_PACKAGE); + int queryFlag = PackageManager.GET_SERVICES; + String testAppName = SystemProperties.get(PROP_ROR_PROVIDER_PACKAGE, ""); + if (!testAppName.isEmpty()) { + Slog.i(TAG, "Using test app: " + testAppName); + intent.setPackage(testAppName); + } else { + queryFlag |= PackageManager.MATCH_SYSTEM_ONLY; + if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) { + intent.setPackage(PROVIDER_PACKAGE); + } } - List<ResolveInfo> resolvedIntents = - mPackageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY); + List<ResolveInfo> resolvedIntents = mPackageManager.queryIntentServices(intent, queryFlag); for (ResolveInfo resolvedInfo : resolvedIntents) { if (resolvedInfo.serviceInfo != null && PROVIDER_REQUIRED_PERMISSION.equals(resolvedInfo.serviceInfo.permission)) { @@ -120,6 +132,7 @@ public class ResumeOnRebootServiceProvider { if (mServiceConnection != null) { mContext.unbindService(mServiceConnection); } + mBinder = null; } /** Bind to the service */ diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 74111be419b5..c462a9274117 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -55,7 +55,9 @@ import android.view.KeyEvent; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * This is the system implementation of a Session. Apps will interact with the @@ -120,8 +122,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR private final Context mContext; private final Object mLock = new Object(); - private final ArrayList<ISessionControllerCallbackHolder> mControllerCallbackHolders = - new ArrayList<>(); + private final CopyOnWriteArrayList<ISessionControllerCallbackHolder> + mControllerCallbackHolders = new CopyOnWriteArrayList<>(); private long mFlags; private MediaButtonReceiverHolder mMediaButtonReceiverHolder; @@ -545,51 +547,66 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } private void pushPlaybackStateUpdate() { + PlaybackState playbackState; synchronized (mLock) { if (mDestroyed) { return; } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onPlaybackStateChanged(mPlaybackState); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushPlaybackStateUpdate", - holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushPlaybackStateUpdate", - holder, e); + playbackState = mPlaybackState; + } + Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onPlaybackStateChanged(playbackState); + } catch (DeadObjectException e) { + if (deadCallbackHolders == null) { + deadCallbackHolders = new ArrayList<>(); } + deadCallbackHolders.add(holder); + logCallbackException("Removing dead callback in pushPlaybackStateUpdate", holder, + e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushPlaybackStateUpdate", holder, e); } } + if (deadCallbackHolders != null) { + mControllerCallbackHolders.removeAll(deadCallbackHolders); + } } private void pushMetadataUpdate() { + MediaMetadata metadata; synchronized (mLock) { if (mDestroyed) { return; } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onMetadataChanged(mMetadata); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushMetadataUpdate", holder, e); + metadata = mMetadata; + } + Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onMetadataChanged(metadata); + } catch (DeadObjectException e) { + if (deadCallbackHolders == null) { + deadCallbackHolders = new ArrayList<>(); } + deadCallbackHolders.add(holder); + logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushMetadataUpdate", holder, e); } } + if (deadCallbackHolders != null) { + mControllerCallbackHolders.removeAll(deadCallbackHolders); + } } private void pushQueueUpdate() { + ParceledListSlice<QueueItem> parcelableQueue; synchronized (mLock) { if (mDestroyed) { return; } - ParceledListSlice<QueueItem> parcelableQueue; if (mQueue == null) { parcelableQueue = null; } else { @@ -598,77 +615,105 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR // as onQueueChanged is an async binder call. parcelableQueue.setInlineCountLimit(1); } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onQueueChanged(parcelableQueue); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushQueueUpdate", holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushQueueUpdate", holder, e); + } + Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onQueueChanged(parcelableQueue); + } catch (DeadObjectException e) { + if (deadCallbackHolders == null) { + deadCallbackHolders = new ArrayList<>(); } + deadCallbackHolders.add(holder); + logCallbackException("Removing dead callback in pushQueueUpdate", holder, e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushQueueUpdate", holder, e); } } + if (deadCallbackHolders != null) { + mControllerCallbackHolders.removeAll(deadCallbackHolders); + } } private void pushQueueTitleUpdate() { + CharSequence queueTitle; synchronized (mLock) { if (mDestroyed) { return; } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onQueueTitleChanged(mQueueTitle); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushQueueTitleUpdate", - holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushQueueTitleUpdate", holder, e); + queueTitle = mQueueTitle; + } + Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onQueueTitleChanged(queueTitle); + } catch (DeadObjectException e) { + if (deadCallbackHolders == null) { + deadCallbackHolders = new ArrayList<>(); } + deadCallbackHolders.add(holder); + logCallbackException("Removing dead callback in pushQueueTitleUpdate", holder, e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushQueueTitleUpdate", holder, e); } } + if (deadCallbackHolders != null) { + mControllerCallbackHolders.removeAll(deadCallbackHolders); + } } private void pushExtrasUpdate() { + Bundle extras; synchronized (mLock) { if (mDestroyed) { return; } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onExtrasChanged(mExtras); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushExtrasUpdate", holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushExtrasUpdate", holder, e); + extras = mExtras; + } + Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onExtrasChanged(extras); + } catch (DeadObjectException e) { + if (deadCallbackHolders == null) { + deadCallbackHolders = new ArrayList<>(); } + deadCallbackHolders.add(holder); + logCallbackException("Removing dead callback in pushExtrasUpdate", holder, e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushExtrasUpdate", holder, e); } } + if (deadCallbackHolders != null) { + mControllerCallbackHolders.removeAll(deadCallbackHolders); + } } private void pushVolumeUpdate() { + PlaybackInfo info; synchronized (mLock) { if (mDestroyed) { return; } - PlaybackInfo info = getVolumeAttributes(); - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onVolumeInfoChanged(info); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushVolumeUpdate", holder, e); + info = getVolumeAttributes(); + } + Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onVolumeInfoChanged(info); + } catch (DeadObjectException e) { + if (deadCallbackHolders == null) { + deadCallbackHolders = new ArrayList<>(); } + deadCallbackHolders.add(holder); + logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushVolumeUpdate", holder, e); } } + if (deadCallbackHolders != null) { + mControllerCallbackHolders.removeAll(deadCallbackHolders); + } } private void pushEvent(String event, Bundle data) { @@ -676,18 +721,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (mDestroyed) { return; } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onEvent(event, data); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushEvent", holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushEvent", holder, e); + } + Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onEvent(event, data); + } catch (DeadObjectException e) { + if (deadCallbackHolders == null) { + deadCallbackHolders = new ArrayList<>(); } + deadCallbackHolders.add(holder); + logCallbackException("Removing dead callback in pushEvent", holder, e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushEvent", holder, e); } } + if (deadCallbackHolders != null) { + mControllerCallbackHolders.removeAll(deadCallbackHolders); + } } private void pushSessionDestroyed() { @@ -697,21 +748,18 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (!mDestroyed) { return; } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onSessionDestroyed(); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removing dead callback in pushSessionDestroyed", - holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushSessionDestroyed", holder, e); - } + } + for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + try { + holder.mCallback.onSessionDestroyed(); + } catch (DeadObjectException e) { + logCallbackException("Removing dead callback in pushSessionDestroyed", holder, e); + } catch (RemoteException e) { + logCallbackException("unexpected exception in pushSessionDestroyed", holder, e); } - // After notifying clear all listeners - mControllerCallbackHolders.clear(); } + // After notifying clear all listeners + mControllerCallbackHolders.clear(); } private PlaybackState getStateWithUpdatedPosition() { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4c3dfbff3f87..5703ffec21a0 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -110,6 +110,8 @@ import static com.android.internal.util.Preconditions.checkArgument; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER; +import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER; +import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT; import static com.android.server.utils.PriorityDump.PRIORITY_ARG; import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL; import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL; @@ -284,7 +286,6 @@ import com.android.server.notification.toast.CustomToastRecord; import com.android.server.notification.toast.TextToastRecord; import com.android.server.notification.toast.ToastRecord; import com.android.server.pm.PackageManagerService; -import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.quota.MultiRateLimiter; @@ -358,7 +359,7 @@ public class NotificationManagerService extends SystemService { private static final int MESSAGE_RECONSIDER_RANKING = 1000; private static final int MESSAGE_RANKING_SORT = 1001; - static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT; + static final int LONG_DELAY = TOAST_WINDOW_TIMEOUT - TOAST_WINDOW_ANIM_BUFFER; // 3.5 seconds static final int SHORT_DELAY = 2000; // 2 seconds // 1 second past the ANR timeout. @@ -3056,7 +3057,7 @@ public class NotificationManagerService extends SystemService { // If the callback fails, this will remove it from the list, so don't // assume that it's valid after this. if (index == 0) { - showNextToastLocked(); + showNextToastLocked(false); } } finally { Binder.restoreCallingIdentity(callingId); @@ -7392,7 +7393,7 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mToastQueue") - void showNextToastLocked() { + void showNextToastLocked(boolean lastToastWasTextRecord) { if (mIsCurrentToastShown) { return; // Don't show the same toast twice. } @@ -7406,7 +7407,7 @@ public class NotificationManagerService extends SystemService { mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG); if (tryShowToast(record, rateLimitingEnabled, isWithinQuota)) { - scheduleDurationReachedLocked(record); + scheduleDurationReachedLocked(record, lastToastWasTextRecord); mIsCurrentToastShown = true; if (rateLimitingEnabled) { mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); @@ -7477,7 +7478,7 @@ public class NotificationManagerService extends SystemService { // Show the next one. If the callback fails, this will remove // it from the list, so don't assume that the list hasn't changed // after this point. - showNextToastLocked(); + showNextToastLocked(lastToast instanceof TextToastRecord); } } @@ -7491,7 +7492,7 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mToastQueue") - private void scheduleDurationReachedLocked(ToastRecord r) + private void scheduleDurationReachedLocked(ToastRecord r, boolean lastToastWasTextRecord) { mHandler.removeCallbacksAndMessages(r); Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r); @@ -7501,6 +7502,14 @@ public class NotificationManagerService extends SystemService { // preference. delay = mAccessibilityManager.getRecommendedTimeoutMillis(delay, AccessibilityManager.FLAG_CONTENT_TEXT); + + if (lastToastWasTextRecord) { + delay += 250; // delay to account for previous toast's "out" animation + } + if (r instanceof TextToastRecord) { + delay += 333; // delay to account for this toast's "in" animation + } + mHandler.sendMessageDelayed(m, delay); } diff --git a/services/core/java/com/android/server/om/DumpState.java b/services/core/java/com/android/server/om/DumpState.java index 1e2e054bbc6c..88afcb2f5041 100644 --- a/services/core/java/com/android/server/om/DumpState.java +++ b/services/core/java/com/android/server/om/DumpState.java @@ -18,6 +18,7 @@ package com.android.server.om; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.om.OverlayIdentifier; import android.os.UserHandle; /** @@ -26,6 +27,7 @@ import android.os.UserHandle; public final class DumpState { @UserIdInt private int mUserId = UserHandle.USER_ALL; @Nullable private String mPackageName; + @Nullable private String mOverlayName; @Nullable private String mField; private boolean mVerbose; @@ -38,14 +40,19 @@ public final class DumpState { } /** Sets the name of the package to dump the state for */ - public void setPackageName(String packageName) { - mPackageName = packageName; + public void setOverlyIdentifier(String overlayIdentifier) { + final OverlayIdentifier overlay = OverlayIdentifier.fromString(overlayIdentifier); + mPackageName = overlay.getPackageName(); + mOverlayName = overlay.getOverlayName(); } @Nullable public String getPackageName() { return mPackageName; } + @Nullable public String getOverlayName() { + return mOverlayName; + } - /** Sets the name of the field to dump the state of */ + /** Sets the name of the field to dump the state for */ public void setField(String field) { mField = field; } diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java index 19fa9204935d..2ebc8edb6740 100644 --- a/services/core/java/com/android/server/om/IdmapDaemon.java +++ b/services/core/java/com/android/server/om/IdmapDaemon.java @@ -20,17 +20,23 @@ import static android.content.Context.IDMAP_SERVICE; import static com.android.server.om.OverlayManagerService.TAG; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.FabricatedOverlayInfo; +import android.os.FabricatedOverlayInternal; import android.os.IBinder; import android.os.IIdmap2; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemService; +import android.text.TextUtils; import android.util.Slog; import com.android.server.FgThread; import java.io.File; +import java.util.List; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; @@ -104,10 +110,12 @@ class IdmapDaemon { return sInstance; } - String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce, - int userId) throws TimeoutException, RemoteException { + String createIdmap(@NonNull String targetPath, @NonNull String overlayPath, + @Nullable String overlayName, int policies, boolean enforce, int userId) + throws TimeoutException, RemoteException { try (Connection c = connect()) { - return mService.createIdmap(targetPath, overlayPath, policies, enforce, userId); + return mService.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName), + policies, enforce, userId); } } @@ -117,11 +125,12 @@ class IdmapDaemon { } } - boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce, - int userId) + boolean verifyIdmap(@NonNull String targetPath, @NonNull String overlayPath, + @Nullable String overlayName, int policies, boolean enforce, int userId) throws Exception { try (Connection c = connect()) { - return mService.verifyIdmap(targetPath, overlayPath, policies, enforce, userId); + return mService.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName), + policies, enforce, userId); } } @@ -129,12 +138,38 @@ class IdmapDaemon { try (Connection c = connect()) { return new File(mService.getIdmapPath(overlayPath, userId)).isFile(); } catch (Exception e) { - Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath + ": " - + e.getMessage()); + Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath, e); return false; } } + FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) { + try (Connection c = connect()) { + return mService.createFabricatedOverlay(overlay); + } catch (Exception e) { + Slog.wtf(TAG, "failed to fabricate overlay " + overlay, e); + return null; + } + } + + boolean deleteFabricatedOverlay(@NonNull String path) { + try (Connection c = connect()) { + return mService.deleteFabricatedOverlay(path); + } catch (Exception e) { + Slog.wtf(TAG, "failed to delete fabricated overlay '" + path + "'", e); + return false; + } + } + + List<FabricatedOverlayInfo> getFabricatedOverlayInfos() { + try (Connection c = connect()) { + return mService.getFabricatedOverlayInfos(); + } catch (Exception e) { + Slog.wtf(TAG, "failed to get fabricated overlays", e); + return null; + } + } + private IBinder getIdmapService() throws TimeoutException, RemoteException { SystemService.start(IDMAP_DAEMON); diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index eeb26552e63d..64362c9b1d69 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -22,15 +22,19 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.content.om.OverlayInfo; import android.content.om.OverlayableInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; +import android.content.pm.PackageParser; import android.os.Build.VERSION_CODES; +import android.os.FabricatedOverlayInfo; +import android.os.FabricatedOverlayInternal; import android.os.OverlayablePolicy; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Slog; +import com.android.server.pm.parsing.pkg.AndroidPackage; + import java.io.IOException; +import java.util.List; /** * Handle the creation and deletion of idmap files. @@ -74,25 +78,26 @@ final class IdmapManager { * Creates the idmap for the target/overlay combination and returns whether the idmap file was * modified. */ - boolean createIdmap(@NonNull final PackageInfo targetPackage, - @NonNull final PackageInfo overlayPackage, int userId) { + boolean createIdmap(@NonNull final AndroidPackage targetPackage, + @NonNull final AndroidPackage overlayPackage, String overlayBasePath, + String overlayName, int userId) { if (DEBUG) { - Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and " - + overlayPackage.packageName); + Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and " + + overlayPackage.getPackageName()); } - final String targetPath = targetPackage.applicationInfo.getBaseCodePath(); - final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath(); + final String targetPath = targetPackage.getBaseApkPath(); try { int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId); boolean enforce = enforceOverlayable(overlayPackage); - if (mIdmapDaemon.verifyIdmap(targetPath, overlayPath, policies, enforce, userId)) { + if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies, + enforce, userId)) { return false; } - return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies, + return mIdmapDaemon.createIdmap(targetPath, overlayBasePath, overlayName, policies, enforce, userId) != null; } catch (Exception e) { Slog.w(TAG, "failed to generate idmap for " + targetPath + " and " - + overlayPath + ": " + e.getMessage()); + + overlayBasePath, e); return false; } } @@ -104,7 +109,7 @@ final class IdmapManager { try { return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId); } catch (Exception e) { - Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage()); + Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath, e); return false; } } @@ -113,22 +118,40 @@ final class IdmapManager { return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId); } - boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) { - return mIdmapDaemon.idmapExists(overlayPackage.applicationInfo.getBaseCodePath(), userId); + /** + * @return the list of all fabricated overlays + */ + List<FabricatedOverlayInfo> getFabricatedOverlayInfos() { + return mIdmapDaemon.getFabricatedOverlayInfos(); + } + + /** + * Creates a fabricated overlay and persists it to disk. + * @return the path to the fabricated overlay + */ + FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) { + return mIdmapDaemon.createFabricatedOverlay(overlay); + } + + /** + * Deletes the fabricated overlay file on disk. + * @return whether the path was deleted + */ + boolean deleteFabricatedOverlay(@NonNull String path) { + return mIdmapDaemon.deleteFabricatedOverlay(path); } /** * Checks if overlayable and policies should be enforced on the specified overlay for backwards * compatibility with pre-Q overlays. */ - private boolean enforceOverlayable(@NonNull final PackageInfo overlayPackage) { - final ApplicationInfo ai = overlayPackage.applicationInfo; - if (ai.targetSdkVersion >= VERSION_CODES.Q) { + private boolean enforceOverlayable(@NonNull final AndroidPackage overlayPackage) { + if (overlayPackage.getTargetSdkVersion() >= VERSION_CODES.Q) { // Always enforce policies for overlays targeting Q+. return true; } - if (ai.isVendor()) { + if (overlayPackage.isVendor()) { // If the overlay is on a pre-Q vendor partition, do not enforce overlayable // restrictions on this overlay because the pre-Q platform has no understanding of // overlayable. @@ -137,20 +160,19 @@ final class IdmapManager { // Do not enforce overlayable restrictions on pre-Q overlays that are signed with the // platform signature or that are preinstalled. - return !(ai.isSystemApp() || ai.isSignedWithPlatformKey()); + return !(overlayPackage.isSystem() || overlayPackage.isSignedWithPlatformKey()); } /** * Retrieves a bitmask for idmap2 that represents the policies the overlay fulfills. */ - private int calculateFulfilledPolicies(@NonNull final PackageInfo targetPackage, - @NonNull final PackageInfo overlayPackage, int userId) { - final ApplicationInfo ai = overlayPackage.applicationInfo; + private int calculateFulfilledPolicies(@NonNull final AndroidPackage targetPackage, + @NonNull final AndroidPackage overlayPackage, int userId) { int fulfilledPolicies = OverlayablePolicy.PUBLIC; // Overlay matches target signature - if (mPackageManager.signaturesMatching(targetPackage.packageName, - overlayPackage.packageName, userId)) { + if (mPackageManager.signaturesMatching(targetPackage.getPackageName(), + overlayPackage.getPackageName(), userId)) { fulfilledPolicies |= OverlayablePolicy.SIGNATURE; } @@ -164,52 +186,52 @@ final class IdmapManager { // preinstalled package, check if overlay matches its signature. if (!TextUtils.isEmpty(mConfigSignaturePackage) && mPackageManager.signaturesMatching(mConfigSignaturePackage, - overlayPackage.packageName, + overlayPackage.getPackageName(), userId)) { fulfilledPolicies |= OverlayablePolicy.CONFIG_SIGNATURE; } // Vendor partition (/vendor) - if (ai.isVendor()) { + if (overlayPackage.isVendor()) { return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION; } // Product partition (/product) - if (ai.isProduct()) { + if (overlayPackage.isProduct()) { return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION; } // Odm partition (/odm) - if (ai.isOdm()) { + if (overlayPackage.isOdm()) { return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION; } // Oem partition (/oem) - if (ai.isOem()) { + if (overlayPackage.isOem()) { return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION; } // System_ext partition (/system_ext) is considered as system // Check this last since every partition except for data is scanned as system in the PMS. - if (ai.isSystemApp() || ai.isSystemExt()) { + if (overlayPackage.isSystem() || overlayPackage.isSystemExt()) { return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION; } return fulfilledPolicies; } - private boolean matchesActorSignature(@NonNull PackageInfo targetPackage, - @NonNull PackageInfo overlayPackage, int userId) { - String targetOverlayableName = overlayPackage.targetOverlayableName; + private boolean matchesActorSignature(@NonNull AndroidPackage targetPackage, + @NonNull AndroidPackage overlayPackage, int userId) { + String targetOverlayableName = overlayPackage.getOverlayTargetName(); if (targetOverlayableName != null) { try { OverlayableInfo overlayableInfo = mPackageManager.getOverlayableForTarget( - targetPackage.packageName, targetOverlayableName, userId); + targetPackage.getPackageName(), targetOverlayableName, userId); if (overlayableInfo != null && overlayableInfo.actor != null) { String actorPackageName = OverlayActorEnforcer.getPackageNameForActor( overlayableInfo.actor, mPackageManager.getNamedActors()).first; if (mPackageManager.signaturesMatching(actorPackageName, - overlayPackage.packageName, userId)) { + overlayPackage.getPackageName(), userId)) { return true; } } diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java index 8121a43e9060..2d540de69660 100644 --- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java +++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java @@ -19,8 +19,6 @@ package com.android.server.om; import android.annotation.NonNull; import android.content.om.OverlayInfo; import android.content.om.OverlayableInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.net.Uri; import android.os.Process; import android.text.TextUtils; @@ -29,6 +27,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.IOException; import java.util.List; @@ -114,12 +113,13 @@ public class OverlayActorEnforcer { } final String targetPackageName = overlayInfo.targetPackageName; - final PackageInfo targetPkgInfo = mPackageManager.getPackageInfo(targetPackageName, userId); + final AndroidPackage targetPkgInfo = mPackageManager.getPackageForUser(targetPackageName, + userId); if (targetPkgInfo == null) { return ActorState.TARGET_NOT_FOUND; } - if ((targetPkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + if (targetPkgInfo.isDebuggable()) { return ActorState.ALLOWED; } @@ -189,23 +189,18 @@ public class OverlayActorEnforcer { return actorUriState; } - String packageName = actorUriPair.first; - PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, userId); - if (packageInfo == null) { - return ActorState.MISSING_APP_INFO; - } - - ApplicationInfo appInfo = packageInfo.applicationInfo; - if (appInfo == null) { - return ActorState.MISSING_APP_INFO; + String actorPackageName = actorUriPair.first; + AndroidPackage actorPackage = mPackageManager.getPackageForUser(actorPackageName, userId); + if (actorPackage == null) { + return ActorState.ACTOR_NOT_FOUND; } // Currently only pre-installed apps can be actors - if (!appInfo.isSystemApp()) { + if (!actorPackage.isSystem()) { return ActorState.ACTOR_NOT_PREINSTALLED; } - if (ArrayUtils.contains(callingPackageNames, packageName)) { + if (ArrayUtils.contains(callingPackageNames, actorPackageName)) { return ActorState.ALLOWED; } @@ -231,7 +226,7 @@ public class OverlayActorEnforcer { NO_NAMED_ACTORS, MISSING_NAMESPACE, MISSING_ACTOR_NAME, - MISSING_APP_INFO, + ACTOR_NOT_FOUND, ACTOR_NOT_PREINSTALLED, INVALID_ACTOR, ALLOWED diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index fd2fb1fcab39..905733cf5c9b 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -24,8 +24,10 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_REASON; +import static android.content.om.OverlayManagerTransaction.Request.TYPE_REGISTER_FABRICATED; import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED; import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED; +import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED; import static android.content.pm.PackageManager.SIGNATURE_MATCH; import static android.os.Trace.TRACE_TAG_RRO; import static android.os.Trace.traceBegin; @@ -43,11 +45,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.om.IOverlayManager; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManagerTransaction; import android.content.om.OverlayableInfo; import android.content.pm.IPackageManager; -import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.content.pm.overlay.OverlayPaths; @@ -55,8 +57,10 @@ import android.content.res.ApkAssets; import android.net.Uri; import android.os.Binder; import android.os.Environment; +import android.os.FabricatedOverlayInternal; import android.os.HandlerThread; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -71,12 +75,15 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.content.om.OverlayConfig; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; + import com.android.server.pm.UserManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackage; import libcore.util.EmptyArray; @@ -92,13 +99,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.Consumer; /** * Service to manage asset overlays. @@ -247,15 +253,6 @@ public final class OverlayManagerService extends SystemService { private final OverlayActorEnforcer mActorEnforcer; - private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> { - persistSettings(); - FgThread.getHandler().post(() -> { - List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId); - updateActivityManager(affectedTargets, pair.userId); - broadcastActionOverlayChanged(pair.packageName, pair.userId); - }); - }; - public OverlayManagerService(@NonNull final Context context) { super(context); try { @@ -315,8 +312,7 @@ public final class OverlayManagerService extends SystemService { // Initialize any users that can't be switched to, as their state would // never be setup in onSwitchUser(). We will switch to the system user right // after this, and its state will be setup there. - final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id); - updatePackageManager(targets, users.get(i).id); + updatePackageManager(mImpl.updateOverlaysForUser(users.get(i).id)); } } } @@ -333,9 +329,7 @@ public final class OverlayManagerService extends SystemService { // ensure overlays in the settings are up-to-date, and propagate // any asset changes to the rest of the system synchronized (mLock) { - final List<String> targets = mImpl.updateOverlaysForUser(newUserId); - final List<String> affectedTargets = updatePackageManager(targets, newUserId); - updateActivityManager(affectedTargets, newUserId); + updateActivityManager(updatePackageManager(mImpl.updateOverlaysForUser(newUserId))); } persistSettings(); } finally { @@ -417,19 +411,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName); for (final int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null && !pi.applicationInfo.isInstantApp()) { - mPackageManager.cachePackageInfo(packageName, userId, pi); - + final AndroidPackage pkg = mPackageManager.onPackageAdded( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageAdded(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageAdded(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } + updateTargetPackages(mImpl.onPackageAdded(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageAdded internal error", e); } @@ -447,19 +433,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName); for (int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null && pi.applicationInfo.isInstantApp()) { - mPackageManager.cachePackageInfo(packageName, userId, pi); - + final AndroidPackage pkg = mPackageManager.onPackageUpdated( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageChanged(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageChanged(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } + updateTargetPackages(mImpl.onPackageChanged(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageChanged internal error", e); } @@ -477,12 +455,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName); for (int userId : userIds) { synchronized (mLock) { - mPackageManager.forgetPackageInfo(packageName, userId); - final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - if (oi != null) { + final AndroidPackage pkg = mPackageManager.onPackageUpdated( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - mImpl.onOverlayPackageReplacing(packageName, userId) - .ifPresent(mPropagateOverlayChange); + updateTargetPackages(mImpl.onPackageReplacing(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageReplacing internal error", e); } @@ -500,18 +477,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName); for (int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null && !pi.applicationInfo.isInstantApp()) { - mPackageManager.cachePackageInfo(packageName, userId, pi); + final AndroidPackage pkg = mPackageManager.onPackageUpdated( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageReplaced(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageReplaced(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } + updateTargetPackages(mImpl.onPackageReplaced(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageReplaced internal error", e); } @@ -529,20 +499,8 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName); for (int userId : userIds) { synchronized (mLock) { - mPackageManager.forgetPackageInfo(packageName, userId); - final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - - try { - if (oi != null) { - mImpl.onOverlayPackageRemoved(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageRemoved(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } - } catch (OperationFailedException e) { - Slog.e(TAG, "onPackageRemoved internal error", e); - } + mPackageManager.onPackageRemoved(packageName, userId); + updateTargetPackages(mImpl.onPackageRemoved(packageName, userId)); } } } finally { @@ -560,11 +518,9 @@ public final class OverlayManagerService extends SystemService { if (userId != UserHandle.USER_NULL) { try { traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED"); - final ArrayList<String> targets; synchronized (mLock) { - targets = mImpl.updateOverlaysForUser(userId); + updatePackageManager(mImpl.updateOverlaysForUser(userId)); } - updatePackageManager(targets, userId); } finally { traceEnd(TRACE_TAG_RRO); } @@ -628,16 +584,22 @@ public final class OverlayManagerService extends SystemService { @Override public OverlayInfo getOverlayInfo(@Nullable final String packageName, final int userIdArg) { - if (packageName == null) { + return getOverlayInfoByIdentifier(new OverlayIdentifier(packageName), userIdArg); + } + + @Override + public OverlayInfo getOverlayInfoByIdentifier(@Nullable final OverlayIdentifier overlay, + final int userIdArg) { + if (overlay == null || overlay.getPackageName() == null) { return null; } try { - traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName); + traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + overlay); final int realUserId = handleIncomingUser(userIdArg, "getOverlayInfo"); synchronized (mLock) { - return mImpl.getOverlayInfo(packageName, realUserId); + return mImpl.getOverlayInfo(overlay, realUserId); } } finally { traceEnd(TRACE_TAG_RRO); @@ -653,15 +615,16 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setEnabled"); - enforceActor(packageName, "setEnabled", realUserId); + enforceActor(overlay, "setEnabled", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setEnabled(packageName, enable, realUserId) - .ifPresent(mPropagateOverlayChange); + updateTargetPackages(mImpl.setEnabled(overlay, enable, realUserId)); return true; } catch (OperationFailedException e) { return false; @@ -684,16 +647,18 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setEnabledExclusive"); - enforceActor(packageName, "setEnabledExclusive", realUserId); + enforceActor(overlay, "setEnabledExclusive", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setEnabledExclusive(packageName, + mImpl.setEnabledExclusive(overlay, false /* withinCategory */, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -716,17 +681,19 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setEnabledExclusiveInCategory"); - enforceActor(packageName, "setEnabledExclusiveInCategory", realUserId); + enforceActor(overlay, "setEnabledExclusiveInCategory", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setEnabledExclusive(packageName, + mImpl.setEnabledExclusive(overlay, true /* withinCategory */, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -750,15 +717,18 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " " + parentPackageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); + final OverlayIdentifier parentOverlay = new OverlayIdentifier(parentPackageName); final int realUserId = handleIncomingUser(userIdArg, "setPriority"); - enforceActor(packageName, "setPriority", realUserId); + enforceActor(overlay, "setPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setPriority(packageName, parentPackageName, realUserId) - .ifPresent(mPropagateOverlayChange); + mImpl.setPriority(overlay, parentOverlay, realUserId) + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -780,15 +750,16 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setHighestPriority"); - enforceActor(packageName, "setHighestPriority", realUserId); + enforceActor(overlay, "setHighestPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setHighestPriority(packageName, realUserId) - .ifPresent(mPropagateOverlayChange); + updateTargetPackages(mImpl.setHighestPriority(overlay, realUserId)); return true; } catch (OperationFailedException e) { return false; @@ -810,15 +781,17 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setLowestPriority"); - enforceActor(packageName, "setLowestPriority", realUserId); + enforceActor(overlay, "setLowestPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setLowestPriority(packageName, realUserId) - .ifPresent(mPropagateOverlayChange); + mImpl.setLowestPriority(overlay, realUserId) + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -858,12 +831,17 @@ public final class OverlayManagerService extends SystemService { return; } + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "invalidateCachesForOverlay"); - enforceActor(packageName, "invalidateCachesForOverlay", realUserId); + enforceActor(overlay, "invalidateCachesForOverlay", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - mImpl.removeIdmapForOverlay(packageName, realUserId); + try { + mImpl.removeIdmapForOverlay(overlay, realUserId); + } catch (OperationFailedException e) { + Slog.w(TAG, "invalidate caches for overlay '" + overlay + "' failed", e); + } } } finally { Binder.restoreCallingIdentity(ident); @@ -893,25 +871,63 @@ public final class OverlayManagerService extends SystemService { } } - private Optional<PackageAndUser> executeRequest( - @NonNull final OverlayManagerTransaction.Request request) throws Exception { - final int realUserId = handleIncomingUser(request.userId, request.typeToString()); - enforceActor(request.packageName, request.typeToString(), realUserId); + private Set<PackageAndUser> executeRequest( + @NonNull final OverlayManagerTransaction.Request request) + throws OperationFailedException { + Objects.requireNonNull(request, "Transaction contains a null request"); + Objects.requireNonNull(request.overlay, + "Transaction overlay identifier must be non-null"); + + final int callingUid = Binder.getCallingUid(); + final int realUserId; + if (request.type == TYPE_REGISTER_FABRICATED + || request.type == TYPE_UNREGISTER_FABRICATED) { + if (request.userId != UserHandle.USER_ALL) { + throw new IllegalArgumentException(request.typeToString() + + " unsupported for user " + request.userId); + } + realUserId = UserHandle.USER_ALL; + + // Enforce that the calling process can only register and unregister fabricated + // overlays using its package name. + final String pkgName = request.overlay.getPackageName(); + if (callingUid != Process.ROOT_UID && !ArrayUtils.contains( + mPackageManager.getPackagesForUid(callingUid), pkgName)) { + throw new IllegalArgumentException("UID " + callingUid + " does own package" + + "name " + pkgName); + } + } else { + // Enforce actor requirements for enabling, disabling, and reordering overlays. + realUserId = handleIncomingUser(request.userId, request.typeToString()); + enforceActor(request.overlay, request.typeToString(), realUserId); + } final long ident = Binder.clearCallingIdentity(); try { switch (request.type) { case TYPE_SET_ENABLED: - Optional<PackageAndUser> opt1 = - mImpl.setEnabled(request.packageName, true, request.userId); - Optional<PackageAndUser> opt2 = - mImpl.setHighestPriority(request.packageName, request.userId); - // Both setEnabled and setHighestPriority affected the same - // target package and user: if both return non-empty - // Optionals, they are identical - return opt1.isPresent() ? opt1 : opt2; + Set<PackageAndUser> result = null; + result = CollectionUtils.addAll(result, + mImpl.setEnabled(request.overlay, true, realUserId)); + result = CollectionUtils.addAll(result, + mImpl.setHighestPriority(request.overlay, realUserId)); + return CollectionUtils.emptyIfNull(result); + case TYPE_SET_DISABLED: - return mImpl.setEnabled(request.packageName, false, request.userId); + return mImpl.setEnabled(request.overlay, false, realUserId); + + case TYPE_REGISTER_FABRICATED: + final FabricatedOverlayInternal fabricated = + request.extras.getParcelable( + OverlayManagerTransaction.Request.BUNDLE_FABRICATED_OVERLAY + ); + Objects.requireNonNull(fabricated, + "no fabricated overlay attached to request"); + return mImpl.registerFabricatedOverlay(fabricated); + + case TYPE_UNREGISTER_FABRICATED: + return mImpl.unregisterFabricatedOverlay(request.overlay); + default: throw new IllegalArgumentException("unsupported request: " + request); } @@ -921,7 +937,7 @@ public final class OverlayManagerService extends SystemService { } private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction) - throws Exception { + throws OperationFailedException { if (DEBUG) { Slog.d(TAG, "commit " + transaction); } @@ -931,25 +947,25 @@ public final class OverlayManagerService extends SystemService { // map: userId -> set<package-name>: target packages of overlays in // this transaction - SparseArray<Set<String>> transactionTargets = new SparseArray<>(); + final SparseArray<Set<String>> transactionTargets = new SparseArray<>(); // map: userId -> set<package-name>: packages that need to reload // their resources due to changes to the overlays in this // transaction - SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>(); + final SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>(); synchronized (mLock) { - // execute the requests (as calling user) for (final OverlayManagerTransaction.Request request : transaction) { - executeRequest(request).ifPresent(target -> { - Set<String> userTargets = transactionTargets.get(target.userId); - if (userTargets == null) { - userTargets = new ArraySet<String>(); - transactionTargets.put(target.userId, userTargets); - } - userTargets.add(target.packageName); - }); + executeRequest(request).forEach( + target -> { + Set<String> userTargets = transactionTargets.get(target.userId); + if (userTargets == null) { + userTargets = new ArraySet<>(); + transactionTargets.put(target.userId, userTargets); + } + userTargets.add(target.packageName); + }); } // past the point of no return: the entire transaction has been @@ -975,10 +991,7 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { // schedule apps to refresh - for (int index = 0; index < affectedPackagesToUpdate.size(); index++) { - final int userId = affectedPackagesToUpdate.keyAt(index); - updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId); - } + updateActivityManager(affectedPackagesToUpdate); // broadcast the ACTION_OVERLAY_CHANGED intents for (int index = 0; index < transactionTargets.size(); index++) { @@ -1059,12 +1072,12 @@ public final class OverlayManagerService extends SystemService { dumpState.setField(arg); break; default: - dumpState.setPackageName(arg); + dumpState.setOverlyIdentifier(arg); break; } } if (dumpState.getPackageName() == null && opti < args.length) { - dumpState.setPackageName(args[opti]); + dumpState.setOverlyIdentifier(args[opti]); opti++; } @@ -1101,12 +1114,12 @@ public final class OverlayManagerService extends SystemService { getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message); } - private void enforceActor(String packageName, String methodName, int realUserId) - throws SecurityException { - OverlayInfo overlayInfo = mImpl.getOverlayInfo(packageName, realUserId); + private void enforceActor(@NonNull OverlayIdentifier overlay, @NonNull String methodName, + int realUserId) throws SecurityException { + OverlayInfo overlayInfo = mImpl.getOverlayInfo(overlay, realUserId); if (overlayInfo == null) { throw new IllegalArgumentException("Unable to retrieve overlay information for " - + packageName); + + overlay); } int callingUid = Binder.getCallingUid(); @@ -1115,7 +1128,13 @@ public final class OverlayManagerService extends SystemService { }; private static final class PackageManagerHelperImpl implements PackageManagerHelper { - + private static class AndroidPackageUsers { + private AndroidPackage mPackage; + private final Set<Integer> mInstalledUsers = new ArraySet<>(); + private AndroidPackageUsers(@NonNull AndroidPackage pkg) { + this.mPackage = pkg; + } + } private final Context mContext; private final IPackageManager mPackageManager; private final PackageManagerInternal mPackageManagerInternal; @@ -1125,7 +1144,8 @@ public final class OverlayManagerService extends SystemService { // intent, querying the PackageManagerService for the actual current // state may lead to contradictions within OMS. Better then to lag // behind until all pending intents have been processed. - private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>(); + private final ArrayMap<String, AndroidPackageUsers> mCache = new ArrayMap<>(); + private final Set<Integer> mInitializedUsers = new ArraySet<>(); PackageManagerHelperImpl(Context context) { mContext = context; @@ -1133,29 +1153,112 @@ public final class OverlayManagerService extends SystemService { mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); } - public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId, - final boolean useCache) { - if (useCache) { - final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId); - if (cachedPi != null) { - return cachedPi; + /** + * Initializes the helper for the user. This only needs to be invoked one time before + * packages of this user are queried. + * @param userId the user id to initialize + * @return a map of package name to all packages installed in the user + */ + @NonNull + public ArrayMap<String, AndroidPackage> initializeForUser(final int userId) { + if (!mInitializedUsers.contains(userId)) { + mInitializedUsers.add(userId); + mPackageManagerInternal.forEachInstalledPackage( + (pkg) -> addPackageUser(pkg, userId), userId); + } + + final ArrayMap<String, AndroidPackage> userPackages = new ArrayMap<>(); + for (int i = 0, n = mCache.size(); i < n; i++) { + final AndroidPackageUsers pkg = mCache.valueAt(i); + if (pkg.mInstalledUsers.contains(userId)) { + userPackages.put(mCache.keyAt(i), pkg.mPackage); } } + return userPackages; + } + + @Override + @Nullable + public AndroidPackage getPackageForUser(@NonNull final String packageName, + final int userId) { + final AndroidPackageUsers pkg = mCache.get(packageName); + if (pkg != null && pkg.mInstalledUsers.contains(userId)) { + return pkg.mPackage; + } try { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId); - if (useCache && pi != null) { - cachePackageInfo(packageName, userId, pi); + if (!mPackageManager.isPackageAvailable(packageName, userId)) { + return null; } - return pi; } catch (RemoteException e) { - // Intentionally left empty. + Slog.w(TAG, "Failed to check availability of package '" + packageName + + "' for user " + userId, e); + return null; + } + return addPackageUser(packageName, userId); + } + + @NonNull + private AndroidPackage addPackageUser(@NonNull final String packageName, + final int user) { + final AndroidPackage pkg = mPackageManagerInternal.getPackage(packageName); + if (pkg == null) { + Slog.w(TAG, "Android package for '" + packageName + "' could not be found;" + + " continuing as if package was never added", new Throwable()); + return null; + } + return addPackageUser(pkg, user); + } + + @NonNull + private AndroidPackage addPackageUser(@NonNull final AndroidPackage pkg, + final int user) { + AndroidPackageUsers pkgUsers = mCache.get(pkg.getPackageName()); + if (pkgUsers == null) { + pkgUsers = new AndroidPackageUsers(pkg); + mCache.put(pkg.getPackageName(), pkgUsers); + } else { + pkgUsers.mPackage = pkg; + } + pkgUsers.mInstalledUsers.add(user); + return pkgUsers.mPackage; + } + + + @NonNull + private void removePackageUser(@NonNull final String packageName, final int user) { + final AndroidPackageUsers pkgUsers = mCache.get(packageName); + if (pkgUsers == null) { + return; } - return null; + removePackageUser(pkgUsers, user); + } + + @NonNull + private void removePackageUser(@NonNull final AndroidPackageUsers pkg, final int user) { + pkg.mInstalledUsers.remove(user); + if (pkg.mInstalledUsers.isEmpty()) { + mCache.remove(pkg.mPackage.getPackageName()); + } + } + + @Nullable + public AndroidPackage onPackageAdded(@NonNull final String packageName, final int userId) { + return addPackageUser(packageName, userId); + } + + @Nullable + public AndroidPackage onPackageUpdated(@NonNull final String packageName, + final int userId) { + return addPackageUser(packageName, userId); + } + + public void onPackageRemoved(@NonNull final String packageName, final int userId) { + removePackageUser(packageName, userId); } @Override - public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) { - return getPackageInfo(packageName, userId, true); + public boolean isInstantApp(@NonNull final String packageName, final int userId) { + return mPackageManagerInternal.isInstantApp(packageName, userId); } @NonNull @@ -1179,15 +1282,6 @@ public final class OverlayManagerService extends SystemService { } @Override - public List<PackageInfo> getOverlayPackages(final int userId) { - final List<PackageInfo> overlays = mPackageManagerInternal.getOverlayPackages(userId); - for (final PackageInfo info : overlays) { - cachePackageInfo(info.packageName, userId, info); - } - return overlays; - } - - @Override public String getConfigSignaturePackage() { final String[] pkgs = mPackageManagerInternal.getKnownPackageNames( PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE, @@ -1200,16 +1294,14 @@ public final class OverlayManagerService extends SystemService { public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, @NonNull String targetOverlayableName, int userId) throws IOException { - PackageInfo packageInfo = getPackageInfo(packageName, userId); + final AndroidPackage packageInfo = getPackageForUser(packageName, userId); if (packageInfo == null) { throw new IOException("Unable to get target package"); } - String baseCodePath = packageInfo.applicationInfo.getBaseCodePath(); - ApkAssets apkAssets = null; try { - apkAssets = ApkAssets.loadFromPath(baseCodePath); + apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath()); return apkAssets.getOverlayableInfo(targetOverlayableName); } finally { if (apkAssets != null) { @@ -1224,16 +1316,14 @@ public final class OverlayManagerService extends SystemService { @Override public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) throws IOException { - PackageInfo packageInfo = getPackageInfo(targetPackageName, userId); + AndroidPackage packageInfo = getPackageForUser(targetPackageName, userId); if (packageInfo == null) { throw new IOException("Unable to get target package"); } - String baseCodePath = packageInfo.applicationInfo.getBaseCodePath(); - ApkAssets apkAssets = null; try { - apkAssets = ApkAssets.loadFromPath(baseCodePath); + apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath()); return apkAssets.definesOverlayable(); } finally { if (apkAssets != null) { @@ -1250,35 +1340,10 @@ public final class OverlayManagerService extends SystemService { mContext.enforceCallingOrSelfPermission(permission, message); } - public PackageInfo getCachedPackageInfo(@NonNull final String packageName, - final int userId) { - final HashMap<String, PackageInfo> map = mCache.get(userId); - return map == null ? null : map.get(packageName); - } - - public void cachePackageInfo(@NonNull final String packageName, final int userId, - @NonNull final PackageInfo pi) { - HashMap<String, PackageInfo> map = mCache.get(userId); - if (map == null) { - map = new HashMap<>(); - mCache.put(userId, map); - } - map.put(packageName, pi); - } - - public void forgetPackageInfo(@NonNull final String packageName, final int userId) { - final HashMap<String, PackageInfo> map = mCache.get(userId); - if (map == null) { - return; - } - map.remove(packageName); - if (map.isEmpty()) { - mCache.delete(userId); - } - } - public void forgetAllPackageInfos(final int userId) { - mCache.delete(userId); + for (int i = 0, n = mCache.size(); i < n; i++) { + removePackageUser(mCache.valueAt(i), userId); + } } @Nullable @@ -1292,19 +1357,12 @@ public final class OverlayManagerService extends SystemService { } private static final String TAB1 = " "; - private static final String TAB2 = TAB1 + TAB1; public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { - pw.println("PackageInfo cache"); + pw.println("AndroidPackage cache"); if (!dumpState.isVerbose()) { - int count = 0; - final int n = mCache.size(); - for (int i = 0; i < n; i++) { - final int userId = mCache.keyAt(i); - count += mCache.get(userId).size(); - } - pw.println(TAB1 + count + " package(s)"); + pw.println(TAB1 + mCache.size() + " package(s)"); return; } @@ -1313,25 +1371,70 @@ public final class OverlayManagerService extends SystemService { return; } - final int n = mCache.size(); - for (int i = 0; i < n; i++) { - final int userId = mCache.keyAt(i); - pw.println(TAB1 + "User " + userId); - final HashMap<String, PackageInfo> map = mCache.get(userId); - for (Map.Entry<String, PackageInfo> entry : map.entrySet()) { - pw.println(TAB2 + entry.getKey() + ": " + entry.getValue()); - } + for (int i = 0, n = mCache.size(); i < n; i++) { + final String packageName = mCache.keyAt(i); + final AndroidPackageUsers pkg = mCache.valueAt(i); + pw.print(TAB1 + packageName + ": " + pkg.mPackage + " users="); + pw.println(TextUtils.join(", ", pkg.mInstalledUsers)); } } } + private void updateTargetPackages(@Nullable PackageAndUser updatedTarget) { + if (updatedTarget != null) { + updateTargetPackages(Set.of(updatedTarget)); + } + } + + private void updateTargetPackages(@Nullable Set<PackageAndUser> updatedTargets) { + if (CollectionUtils.isEmpty(updatedTargets)) { + return; + } + persistSettings(); + final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(updatedTargets); + FgThread.getHandler().post(() -> { + for (int i = 0, n = userTargets.size(); i < n; i++) { + final ArraySet<String> targets = userTargets.valueAt(i); + final int userId = userTargets.keyAt(i); + + // Update the overlay paths in package manager. + final List<String> affectedPackages = updatePackageManager(targets, userId); + updateActivityManager(affectedPackages, userId); + + // Overlays targeting shared libraries may cause more packages to need to be + // refreshed. + broadcastActionOverlayChanged(targets, userId); + } + }); + } + + @Nullable + private static SparseArray<ArraySet<String>> groupTargetsByUserId( + @Nullable final Set<PackageAndUser> targetsAndUsers) { + final SparseArray<ArraySet<String>> userTargets = new SparseArray<>(); + CollectionUtils.forEach(targetsAndUsers, target -> { + ArraySet<String> targets = userTargets.get(target.userId); + if (targets == null) { + targets = new ArraySet<>(); + userTargets.put(target.userId, targets); + } + targets.add(target.packageName); + }); + return userTargets; + } + // Helper methods to update other parts of the system or read/write // settings: these methods should never call into each other! - private void broadcastActionOverlayChanged(@NonNull final String targetPackageName, + private static void broadcastActionOverlayChanged(@NonNull final Set<String> targetPackages, final int userId) { + CollectionUtils.forEach(targetPackages, + target -> broadcastActionOverlayChanged(target, userId)); + } + + private static void broadcastActionOverlayChanged(String targetPackage, final int userId) { final Intent intent = new Intent(ACTION_OVERLAY_CHANGED, - Uri.fromParts("package", targetPackageName, null)); + Uri.fromParts("package", targetPackage, null)); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); try { ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null, @@ -1345,7 +1448,7 @@ public final class OverlayManagerService extends SystemService { * Tell the activity manager to tell a set of packages to reload their * resources. */ - private void updateActivityManager(List<String> targetPackageNames, final int userId) { + private void updateActivityManager(@NonNull List<String> targetPackageNames, final int userId) { final IActivityManager am = ActivityManager.getService(); try { am.scheduleApplicationInfoChanged(targetPackageNames, userId); @@ -1354,16 +1457,33 @@ public final class OverlayManagerService extends SystemService { } } - private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) { - return updatePackageManager(Collections.singletonList(targetPackageNames), userId); + private void updateActivityManager(@NonNull SparseArray<List<String>> targetPackageNames) { + for (int i = 0, n = targetPackageNames.size(); i < n; i++) { + updateActivityManager(targetPackageNames.valueAt(i), targetPackageNames.keyAt(i)); + } + } + + @NonNull + private SparseArray<List<String>> updatePackageManager(@Nullable Set<PackageAndUser> targets) { + if (CollectionUtils.isEmpty(targets)) { + return new SparseArray<>(); + } + final SparseArray<List<String>> affectedTargets = new SparseArray<>(); + final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(targets); + for (int i = 0, n = userTargets.size(); i < n; i++) { + final int userId = userTargets.keyAt(i); + affectedTargets.put(userId, updatePackageManager(userTargets.valueAt(i), userId)); + } + return affectedTargets; } /** * Updates the target packages' set of enabled overlays in PackageManager. * @return the package names of affected targets (a superset of - * targetPackageNames: the target themserlves and shared libraries) + * targetPackageNames: the target themselves and shared libraries) */ - private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames, + @NonNull + private List<String> updatePackageManager(@NonNull Collection<String> targetPackageNames, final int userId) { try { traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames); diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index c547c36a8033..fb183f57cac4 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -28,26 +28,31 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.om.CriticalOverlayInfo; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.overlay.OverlayPaths; +import android.content.pm.parsing.ParsingPackageUtils; +import android.os.FabricatedOverlayInfo; +import android.os.FabricatedOverlayInternal; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import com.android.internal.content.om.OverlayConfig; -import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Iterator; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; /** * Internal implementation of OverlayManagerService. @@ -85,29 +90,42 @@ final class OverlayManagerServiceImpl { * should either scrap the overlay manager's previous settings or merge the old * settings with the new. */ - private boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth, + private boolean mustReinitializeOverlay(@NonNull final AndroidPackage theTruth, @Nullable final OverlayInfo oldSettings) { if (oldSettings == null) { return true; } - if (!Objects.equals(theTruth.overlayTarget, oldSettings.targetPackageName)) { + if (!Objects.equals(theTruth.getOverlayTarget(), oldSettings.targetPackageName)) { return true; } - if (!Objects.equals(theTruth.targetOverlayableName, oldSettings.targetOverlayableName)) { + if (!Objects.equals(theTruth.getOverlayTargetName(), oldSettings.targetOverlayableName)) { return true; } - - boolean isMutable = isPackageConfiguredMutable(theTruth.packageName); + if (oldSettings.isFabricated) { + return true; + } + boolean isMutable = isPackageConfiguredMutable(theTruth); if (isMutable != oldSettings.isMutable) { return true; } - // If an immutable overlay changes its configured enabled state, reinitialize the overlay. - if (!isMutable && isPackageConfiguredEnabled(theTruth.packageName) - != oldSettings.isEnabled()) { + if (!isMutable && isPackageConfiguredEnabled(theTruth) != oldSettings.isEnabled()) { return true; } + return false; + } + private boolean mustReinitializeOverlay(@NonNull final FabricatedOverlayInfo theTruth, + @Nullable final OverlayInfo oldSettings) { + if (oldSettings == null) { + return true; + } + if (!Objects.equals(theTruth.targetPackageName, oldSettings.targetPackageName)) { + return true; + } + if (!Objects.equals(theTruth.targetOverlayable, oldSettings.targetOverlayableName)) { + return true; + } return false; } @@ -129,87 +147,45 @@ final class OverlayManagerServiceImpl { * of two sets: the set of targets with currently active overlays, and the * set of targets that had, but no longer have, active overlays. */ - ArrayList<String> updateOverlaysForUser(final int newUserId) { + @NonNull + ArraySet<PackageAndUser> updateOverlaysForUser(final int newUserId) { if (DEBUG) { Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId); } - final Set<String> packagesToUpdateAssets = new ArraySet<>(); - final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId); - final int tmpSize = tmp.size(); - final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize); - for (int i = 0; i < tmpSize; i++) { - final List<OverlayInfo> chunk = tmp.valueAt(i); - final int chunkSize = chunk.size(); - for (int j = 0; j < chunkSize; j++) { - final OverlayInfo oi = chunk.get(j); - storedOverlayInfos.put(oi.packageName, oi); - } - } - - // Reset overlays if something critical like the target package name - // has changed - List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId); - final int overlayPackagesSize = overlayPackages.size(); - for (int i = 0; i < overlayPackagesSize; i++) { - final PackageInfo overlayPackage = overlayPackages.get(i); - final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName); - - int priority = getPackageConfiguredPriority(overlayPackage.packageName); - if (mustReinitializeOverlay(overlayPackage, oi)) { - // if targetPackageName has changed the package that *used* to - // be the target must also update its assets - if (oi != null) { - packagesToUpdateAssets.add(oi.targetPackageName); - } - - mSettings.init(overlayPackage.packageName, newUserId, - overlayPackage.overlayTarget, - overlayPackage.targetOverlayableName, - overlayPackage.applicationInfo.getBaseCodePath(), - isPackageConfiguredMutable(overlayPackage.packageName), - isPackageConfiguredEnabled(overlayPackage.packageName), - priority, overlayPackage.overlayCategory); - } else if (priority != oi.priority) { - mSettings.setPriority(overlayPackage.packageName, newUserId, priority); - packagesToUpdateAssets.add(oi.targetPackageName); - } + // Remove the settings of all overlays that are no longer installed for this user. + final ArraySet<PackageAndUser> updatedTargets = new ArraySet<>(); + final ArrayMap<String, AndroidPackage> userPackages = mPackageManager.initializeForUser( + newUserId); + CollectionUtils.addAll(updatedTargets, removeOverlaysForUser( + (info) -> !userPackages.containsKey(info.packageName), newUserId)); - storedOverlayInfos.remove(overlayPackage.packageName); - } - - // any OverlayInfo left in storedOverlayInfos is no longer - // installed and should be removed - final int storedOverlayInfosSize = storedOverlayInfos.size(); - for (int i = 0; i < storedOverlayInfosSize; i++) { - final OverlayInfo oi = storedOverlayInfos.valueAt(i); - mSettings.remove(oi.packageName, oi.userId); - removeIdmapIfPossible(oi); - packagesToUpdateAssets.add(oi.targetPackageName); - } - - // make sure every overlay's state is up-to-date; this needs to happen - // after old overlays have been removed, or we risk removing a - // legitimate idmap file if a new overlay package has the same apk path - // as the removed overlay package used to have - for (int i = 0; i < overlayPackagesSize; i++) { - final PackageInfo overlayPackage = overlayPackages.get(i); + // Update the state of all installed packages containing overlays, and initialize new + // overlays that are not currently in the settings. + for (int i = 0, n = userPackages.size(); i < n; i++) { + final AndroidPackage pkg = userPackages.valueAt(i); try { - updateState(overlayPackage.overlayTarget, overlayPackage.packageName, - newUserId, 0); - } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); - mSettings.remove(overlayPackage.packageName, newUserId); + CollectionUtils.addAll(updatedTargets, + updatePackageOverlays(pkg, newUserId, 0 /* flags */)); + + // When a new user is switched to for the first time, package manager must be + // informed of the overlay paths for all packages installed in the user. + updatedTargets.add(new PackageAndUser(pkg.getPackageName(), newUserId)); + } catch (OperationFailedException e) { + Slog.e(TAG, "failed to initialize overlays of '" + pkg.getPackageName() + + "' for user " + newUserId + "", e); } - packagesToUpdateAssets.add(overlayPackage.overlayTarget); } - // remove target packages that are not installed - final Iterator<String> iter = packagesToUpdateAssets.iterator(); - while (iter.hasNext()) { - String targetPackageName = iter.next(); - if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) { - iter.remove(); + // Update the state of all fabricated overlays, and initialize fabricated overlays in the + // new user. + for (final FabricatedOverlayInfo info : getFabricatedOverlayInfos()) { + try { + CollectionUtils.addAll(updatedTargets, registerFabricatedOverlay( + info, newUserId)); + } catch (OperationFailedException e) { + Slog.e(TAG, "failed to initialize fabricated overlay of '" + info.path + + "' for user " + newUserId + "", e); } } @@ -232,14 +208,20 @@ final class OverlayManagerServiceImpl { // Enable the default overlay if its category does not have a single overlay enabled. for (final String defaultOverlay : mDefaultOverlays) { try { - final OverlayInfo oi = mSettings.getOverlayInfo(defaultOverlay, newUserId); + // OverlayConfig is the new preferred way to enable overlays by default. This legacy + // default enabled method was created before overlays could have a name specified. + // Only allow enabling overlays without a name using this mechanism. + final OverlayIdentifier overlay = new OverlayIdentifier(defaultOverlay); + + final OverlayInfo oi = mSettings.getOverlayInfo(overlay, newUserId); if (!enabledCategories.contains(oi.category)) { Slog.w(TAG, "Enabling default overlay '" + defaultOverlay + "' for target '" + oi.targetPackageName + "' in category '" + oi.category + "' for user " + newUserId); - mSettings.setEnabled(oi.packageName, newUserId, true); - if (updateState(oi.targetPackageName, oi.packageName, newUserId, 0)) { - packagesToUpdateAssets.add(oi.targetPackageName); + mSettings.setEnabled(overlay, newUserId, true); + if (updateState(oi, newUserId, 0)) { + CollectionUtils.add(updatedTargets, + new PackageAndUser(oi.targetPackageName, oi.userId)); } } } catch (OverlayManagerSettings.BadKeyException e) { @@ -248,7 +230,8 @@ final class OverlayManagerServiceImpl { } } - return new ArrayList<>(packagesToUpdateAssets); + cleanStaleResourceCache(); + return updatedTargets; } void onUserRemoved(final int userId) { @@ -258,236 +241,151 @@ final class OverlayManagerServiceImpl { mSettings.removeUser(userId); } - Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName, + @NonNull + Set<PackageAndUser> onPackageAdded(@NonNull final String pkgName, final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId); - } - - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */); } - Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName, + @NonNull + Set<PackageAndUser> onPackageChanged(@NonNull final String pkgName, final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); - } - - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */); } - Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId=" - + userId); - } + @NonNull + Set<PackageAndUser> onPackageReplacing(@NonNull final String pkgName, final int userId) + throws OperationFailedException { + return reconcileSettingsForPackage(pkgName, userId, FLAG_OVERLAY_IS_BEING_REPLACED); + } - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); + @NonNull + Set<PackageAndUser> onPackageReplaced(@NonNull final String pkgName, final int userId) + throws OperationFailedException { + return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */); } - Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName, - final int userId) throws OperationFailedException { + @NonNull + Set<PackageAndUser> onPackageRemoved(@NonNull final String pkgName, final int userId) { if (DEBUG) { - Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId); + Slog.d(TAG, "onPackageRemoved pkgName=" + pkgName + " userId=" + userId); } + // Update the state of all overlays that target this package. + final Set<PackageAndUser> targets = updateOverlaysForTarget(pkgName, userId, 0 /* flags */); - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); + // Remove all the overlays this package declares. + return CollectionUtils.addAll(targets, + removeOverlaysForUser(oi -> pkgName.equals(oi.packageName), userId)); } - Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); - } + @NonNull + private Set<PackageAndUser> removeOverlaysForUser( + @NonNull final Predicate<OverlayInfo> condition, final int userId) { + final List<OverlayInfo> overlays = mSettings.removeIf( + io -> userId == io.userId && condition.test(io) ); + Set<PackageAndUser> targets = Collections.emptySet(); + for (int i = 0, n = overlays.size(); i < n; i++) { + final OverlayInfo info = overlays.get(i); + targets = CollectionUtils.add(targets, + new PackageAndUser(info.targetPackageName, userId)); - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); + // Remove the idmap if the overlay is no longer installed for any user. + removeIdmapIfPossible(info); + } + return targets; } - /** - * Update the state of any overlays for this target. - */ - private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget( - @NonNull final String targetPackageName, final int userId, final int flags) - throws OperationFailedException { - final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName, - userId); - - // Update the state for any overlay that targets this package. + @NonNull + private Set<PackageAndUser> updateOverlaysForTarget(@NonNull final String targetPackage, + final int userId, final int flags) { boolean modified = false; - for (final OverlayInfo oi : targetOverlays) { - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, - userId); - if (overlayPackage == null) { - modified |= mSettings.remove(oi.packageName, oi.userId); - removeIdmapIfPossible(oi); - } else { - try { - modified |= updateState(targetPackageName, oi.packageName, userId, flags); - } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); - modified |= mSettings.remove(oi.packageName, userId); - } + final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackage, userId); + for (int i = 0, n = overlays.size(); i < n; i++) { + final OverlayInfo oi = overlays.get(i); + try { + modified |= updateState(oi, userId, flags); + } catch (OverlayManagerSettings.BadKeyException e) { + Slog.e(TAG, "failed to update settings", e); + modified |= mSettings.remove(oi.getOverlayIdentifier(), userId); } } - if (!modified) { - // Update the overlay paths of the target within package manager if necessary. - final List<String> enabledOverlayPaths = new ArrayList<>(targetOverlays.size()); - - // Framework overlays are first in the overlay paths of a package within PackageManager. - for (final OverlayInfo oi : mSettings.getOverlaysForTarget("android", userId)) { - if (oi.isEnabled()) { - enabledOverlayPaths.add(oi.baseCodePath); - } - } - - for (final OverlayInfo oi : targetOverlays) { - if (oi.isEnabled()) { - enabledOverlayPaths.add(oi.baseCodePath); - } - } - - // TODO(): Use getEnabledOverlayPaths(userId, targetPackageName) instead of - // resourceDirs if in the future resourceDirs contains APKs other than overlays - PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId); - ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo; - String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs; - - // If the lists aren't the same length, the enabled overlays have changed - if (ArrayUtils.size(resourceDirs) != enabledOverlayPaths.size()) { - modified = true; - } else if (resourceDirs != null) { - // If any element isn't equal, an overlay or the order of overlays has changed - for (int index = 0; index < resourceDirs.length; index++) { - if (!resourceDirs[index].equals(enabledOverlayPaths.get(index))) { - modified = true; - break; - } - } - } - } - - if (modified) { - return Optional.of(new PackageAndUser(targetPackageName, userId)); + return Collections.emptySet(); } - return Optional.empty(); + return Set.of(new PackageAndUser(targetPackage, userId)); } - Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId); - } - - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found"); - return onOverlayPackageRemoved(packageName, userId); + @NonNull + private Set<PackageAndUser> updatePackageOverlays(@NonNull AndroidPackage pkg, + final int userId, final int flags) throws OperationFailedException { + if (pkg.getOverlayTarget() == null) { + // This package does not have overlays declared in its manifest. + return Collections.emptySet(); } - mSettings.init(packageName, userId, overlayPackage.overlayTarget, - overlayPackage.targetOverlayableName, - overlayPackage.applicationInfo.getBaseCodePath(), - isPackageConfiguredMutable(overlayPackage.packageName), - isPackageConfiguredEnabled(overlayPackage.packageName), - getPackageConfiguredPriority(overlayPackage.packageName), - overlayPackage.overlayCategory); + Set<PackageAndUser> updatedTargets = Collections.emptySet(); + final OverlayIdentifier overlay = new OverlayIdentifier(pkg.getPackageName()); + final int priority = getPackageConfiguredPriority(pkg); try { - if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); - } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - mSettings.remove(packageName, userId); - throw new OperationFailedException("failed to update settings", e); - } - } - - Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId); - } + OverlayInfo currentInfo = mSettings.getNullableOverlayInfo(overlay, userId); + if (mustReinitializeOverlay(pkg, currentInfo)) { + if (currentInfo != null) { + // If the targetPackageName has changed, the package that *used* to + // be the target must also update its assets. + updatedTargets = CollectionUtils.add(updatedTargets, + new PackageAndUser(currentInfo.targetPackageName, userId)); + } - try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - if (updateState(oi.targetPackageName, packageName, userId, 0)) { - return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); + currentInfo = mSettings.init(overlay, userId, pkg.getOverlayTarget(), + pkg.getOverlayTargetName(), pkg.getBaseApkPath(), + isPackageConfiguredMutable(pkg), + isPackageConfiguredEnabled(pkg), + getPackageConfiguredPriority(pkg), pkg.getOverlayCategory(), + false); + } else if (priority != currentInfo.priority) { + // Changing the priority of an overlay does not cause its settings to be + // reinitialized. Reorder the overlay and update its target package. + mSettings.setPriority(overlay, userId, priority); + updatedTargets = CollectionUtils.add(updatedTargets, + new PackageAndUser(currentInfo.targetPackageName, userId)); } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - throw new OperationFailedException("failed to update settings", e); - } - } - Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId=" - + userId); - } - - try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - if (updateState(oi.targetPackageName, packageName, userId, - FLAG_OVERLAY_IS_BEING_REPLACED)) { - removeIdmapIfPossible(oi); - return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); + // Update the enabled state of the overlay. + if (updateState(currentInfo, userId, flags)) { + updatedTargets = CollectionUtils.add(updatedTargets, + new PackageAndUser(currentInfo.targetPackageName, userId)); } - return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { throw new OperationFailedException("failed to update settings", e); } + return updatedTargets; } - Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName, - final int userId) throws OperationFailedException { + @NonNull + private Set<PackageAndUser> reconcileSettingsForPackage(@NonNull final String pkgName, + final int userId, final int flags) throws OperationFailedException { if (DEBUG) { - Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId=" - + userId); - } - - final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId); - if (pkg == null) { - Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found"); - return onOverlayPackageRemoved(packageName, userId); + Slog.d(TAG, "reconcileSettingsForPackage pkgName=" + pkgName + " userId=" + userId); } - try { - final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId); - if (mustReinitializeOverlay(pkg, oldOi)) { - mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName, - pkg.applicationInfo.getBaseCodePath(), - isPackageConfiguredMutable(pkg.packageName), - isPackageConfiguredEnabled(pkg.packageName), - getPackageConfiguredPriority(pkg.packageName), pkg.overlayCategory); - } + // Update the state of overlays that target this package. + Set<PackageAndUser> updatedTargets = Collections.emptySet(); + updatedTargets = CollectionUtils.addAll(updatedTargets, + updateOverlaysForTarget(pkgName, userId, flags)); - if (updateState(pkg.overlayTarget, packageName, userId, 0)) { - return Optional.of(new PackageAndUser(pkg.overlayTarget, userId)); - } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - throw new OperationFailedException("failed to update settings", e); + // Realign the overlay settings with PackageManager's view of the package. + final AndroidPackage pkg = mPackageManager.getPackageForUser(pkgName, userId); + if (pkg == null) { + return onPackageRemoved(pkgName, userId); } - } - Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName, - final int userId) throws OperationFailedException { - try { - final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId); - if (mSettings.remove(packageName, userId)) { - removeIdmapIfPossible(overlayInfo); - return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); - } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - throw new OperationFailedException("failed to remove overlay", e); - } + // Update the state of the overlays this package declares in its manifest. + updatedTargets = CollectionUtils.addAll(updatedTargets, + updatePackageOverlays(pkg, userId, flags)); + return updatedTargets; } - OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) { + OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier packageName, final int userId) { try { return mSettings.getOverlayInfo(packageName, userId); } catch (OverlayManagerSettings.BadKeyException e) { @@ -504,94 +402,78 @@ final class OverlayManagerServiceImpl { return mSettings.getOverlaysForUser(userId); } - Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable, - final int userId) throws OperationFailedException { + @NonNull + Set<PackageAndUser> setEnabled(@NonNull final OverlayIdentifier overlay, + final boolean enable, final int userId) throws OperationFailedException { if (DEBUG) { - Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d", - packageName, enable, userId)); - } - - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException( - String.format("failed to find overlay package %s for user %d", - packageName, userId)); + Slog.d(TAG, String.format("setEnabled overlay=%s enable=%s userId=%d", + overlay, enable, userId)); } try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); + final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId); if (!oi.isMutable) { // Ignore immutable overlays. throw new OperationFailedException( "cannot enable immutable overlay packages in runtime"); } - boolean modified = mSettings.setEnabled(packageName, userId, enable); - modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0); + boolean modified = mSettings.setEnabled(overlay, userId, enable); + modified |= updateState(oi, userId, 0); if (modified) { - return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); + return Set.of(new PackageAndUser(oi.targetPackageName, userId)); } - return Optional.empty(); + return Set.of(); } catch (OverlayManagerSettings.BadKeyException e) { throw new OperationFailedException("failed to update settings", e); } } - Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName, + Optional<PackageAndUser> setEnabledExclusive(@NonNull final OverlayIdentifier overlay, boolean withinCategory, final int userId) throws OperationFailedException { if (DEBUG) { - Slog.d(TAG, String.format("setEnabledExclusive packageName=%s" - + " withinCategory=%s userId=%d", packageName, withinCategory, userId)); - } - - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); + Slog.d(TAG, String.format("setEnabledExclusive overlay=%s" + + " withinCategory=%s userId=%d", overlay, withinCategory, userId)); } try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - final String targetPackageName = oi.targetPackageName; + final OverlayInfo enabledInfo = mSettings.getOverlayInfo(overlay, userId); + if (!enabledInfo.isMutable) { + throw new OperationFailedException( + "cannot enable immutable overlay packages in runtime"); + } - List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId); + // Remove the overlay to have enabled from the list of overlays to disable. + List<OverlayInfo> allOverlays = getOverlayInfosForTarget(enabledInfo.targetPackageName, + userId); + allOverlays.remove(enabledInfo); boolean modified = false; - - // Disable all other overlays. - allOverlays.remove(oi); for (int i = 0; i < allOverlays.size(); i++) { final OverlayInfo disabledInfo = allOverlays.get(i); - final String disabledOverlayPackageName = disabledInfo.packageName; - final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo( - disabledOverlayPackageName, userId); - if (disabledOverlayPackageInfo == null) { - modified |= mSettings.remove(disabledOverlayPackageName, userId); - continue; - } - + final OverlayIdentifier disabledOverlay = disabledInfo.getOverlayIdentifier(); if (!disabledInfo.isMutable) { // Don't touch immutable overlays. continue; } - if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory, - oi.category)) { + if (withinCategory && !Objects.equals(disabledInfo.category, + enabledInfo.category)) { // Don't touch overlays from other categories. continue; } // Disable the overlay. - modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false); - modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0); + modified |= mSettings.setEnabled(disabledOverlay, userId, false); + modified |= updateState(disabledInfo, userId, 0); } // Enable the selected overlay. - modified |= mSettings.setEnabled(packageName, userId, true); - modified |= updateState(targetPackageName, packageName, userId, 0); + modified |= mSettings.setEnabled(overlay, userId, true); + modified |= updateState(enabledInfo, userId, 0); if (modified) { - return Optional.of(new PackageAndUser(targetPackageName, userId)); + return Optional.of(new PackageAndUser(enabledInfo.targetPackageName, userId)); } return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { @@ -599,87 +481,200 @@ final class OverlayManagerServiceImpl { } } - private boolean isPackageConfiguredMutable(@NonNull final String packageName) { - return mOverlayConfig.isMutable(packageName); - } + @NonNull + Set<PackageAndUser> registerFabricatedOverlay( + @NonNull final FabricatedOverlayInternal overlay) + throws OperationFailedException { + if (ParsingPackageUtils.validateName(overlay.overlayName, + false /* requireSeparator */, true /* requireFilename */) != null) { + throw new OperationFailedException( + "overlay name can only consist of alphanumeric characters, '_', and '.'"); + } - private int getPackageConfiguredPriority(@NonNull final String packageName) { - return mOverlayConfig.getPriority(packageName); - } + final FabricatedOverlayInfo info = mIdmapManager.createFabricatedOverlay(overlay); + if (info == null) { + throw new OperationFailedException("failed to create fabricated overlay"); + } - private boolean isPackageConfiguredEnabled(@NonNull final String packageName) { - return mOverlayConfig.isEnabled(packageName); + final Set<PackageAndUser> updatedTargets = new ArraySet<>(); + for (int userId : mSettings.getUsers()) { + updatedTargets.addAll(registerFabricatedOverlay(info, userId)); + } + return updatedTargets; } - Optional<PackageAndUser> setPriority(@NonNull final String packageName, - @NonNull final String newParentPackageName, final int userId) + @NonNull + private Set<PackageAndUser> registerFabricatedOverlay( + @NonNull final FabricatedOverlayInfo info, int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName=" - + newParentPackageName + " userId=" + userId); + final OverlayIdentifier overlayIdentifier = new OverlayIdentifier( + info.packageName, info.overlayName); + final Set<PackageAndUser> updatedTargets = new ArraySet<>(); + OverlayInfo oi = mSettings.getNullableOverlayInfo(overlayIdentifier, userId); + if (oi != null) { + if (!oi.isFabricated) { + throw new OperationFailedException("non-fabricated overlay with name '" + + oi.overlayName + "' already present in '" + oi.packageName + "'"); + } } - - if (!isPackageConfiguredMutable(packageName)) { - throw new OperationFailedException(String.format( - "overlay package %s user %d is not updatable", packageName, userId)); + try { + if (mustReinitializeOverlay(info, oi)) { + if (oi != null) { + // If the fabricated overlay changes its target package, update the previous + // target package so it no longer is overlaid. + updatedTargets.add(new PackageAndUser(oi.targetPackageName, userId)); + } + oi = mSettings.init(overlayIdentifier, userId, info.targetPackageName, + info.targetOverlayable, info.path, true, false, + OverlayConfig.DEFAULT_PRIORITY, null, true); + } else { + // The only non-critical part of the info that will change is path to the fabricated + // overlay. + mSettings.setBaseCodePath(overlayIdentifier, userId, info.path); + } + if (updateState(oi, userId, 0)) { + updatedTargets.add(new PackageAndUser(oi.targetPackageName, userId)); + } + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); } - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); - } + return updatedTargets; + } - if (mSettings.setPriority(packageName, newParentPackageName, userId)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); + @NonNull + Set<PackageAndUser> unregisterFabricatedOverlay(@NonNull final OverlayIdentifier overlay) { + final Set<PackageAndUser> updatedTargets = new ArraySet<>(); + for (int userId : mSettings.getUsers()) { + updatedTargets.addAll(unregisterFabricatedOverlay(overlay, userId)); } - return Optional.empty(); + return updatedTargets; } - Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId); + @NonNull + private Set<PackageAndUser> unregisterFabricatedOverlay( + @NonNull final OverlayIdentifier overlay, int userId) { + final OverlayInfo oi = mSettings.getNullableOverlayInfo(overlay, userId); + if (oi != null) { + mSettings.remove(overlay, userId); + if (oi.isEnabled()) { + // Removing a fabricated overlay only changes the overlay path of a package if it is + // currently enabled. + return Set.of(new PackageAndUser(oi.targetPackageName, userId)); + } } + return Set.of(); + } - if (!isPackageConfiguredMutable(packageName)) { - throw new OperationFailedException(String.format( - "overlay package %s user %d is not updatable", packageName, userId)); - } - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); + private void cleanStaleResourceCache() { + // Clean up fabricated overlays that are no longer registered in any user. + final Set<String> fabricatedPaths = mSettings.getAllBaseCodePaths(); + for (final FabricatedOverlayInfo info : mIdmapManager.getFabricatedOverlayInfos()) { + if (!fabricatedPaths.contains(info.path)) { + mIdmapManager.deleteFabricatedOverlay(info.path); + } } + } - if (mSettings.setHighestPriority(packageName, userId)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); - } - return Optional.empty(); + /** + * Retrieves information about the fabricated overlays still in use. + * @return + */ + @NonNull + private List<FabricatedOverlayInfo> getFabricatedOverlayInfos() { + final Set<String> fabricatedPaths = mSettings.getAllBaseCodePaths(); + // Filter out stale fabricated overlays. + final ArrayList<FabricatedOverlayInfo> infos = new ArrayList<>( + mIdmapManager.getFabricatedOverlayInfos()); + infos.removeIf(info -> !fabricatedPaths.contains(info.path)); + return infos; + } + + private boolean isPackageConfiguredMutable(@NonNull final AndroidPackage overlay) { + // TODO(162841629): Support overlay name in OverlayConfig + return mOverlayConfig.isMutable(overlay.getPackageName()); } - Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId) + private int getPackageConfiguredPriority(@NonNull final AndroidPackage overlay) { + // TODO(162841629): Support overlay name in OverlayConfig + return mOverlayConfig.getPriority(overlay.getPackageName()); + } + + private boolean isPackageConfiguredEnabled(@NonNull final AndroidPackage overlay) { + // TODO(162841629): Support overlay name in OverlayConfig + return mOverlayConfig.isEnabled(overlay.getPackageName()); + } + + Optional<PackageAndUser> setPriority(@NonNull final OverlayIdentifier overlay, + @NonNull final OverlayIdentifier newParentOverlay, final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId); - } + try { + if (DEBUG) { + Slog.d(TAG, "setPriority overlay=" + overlay + " newParentOverlay=" + + newParentOverlay + " userId=" + userId); + } + + final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId); + if (!overlayInfo.isMutable) { + // Ignore immutable overlays. + throw new OperationFailedException( + "cannot change priority of an immutable overlay package at runtime"); + } - if (!isPackageConfiguredMutable(packageName)) { - throw new OperationFailedException(String.format( - "overlay package %s user %d is not updatable", packageName, userId)); + if (mSettings.setPriority(overlay, newParentOverlay, userId)) { + return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); + } + return Optional.empty(); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); } + } - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); + Set<PackageAndUser> setHighestPriority(@NonNull final OverlayIdentifier overlay, + final int userId) throws OperationFailedException { + try{ + if (DEBUG) { + Slog.d(TAG, "setHighestPriority overlay=" + overlay + " userId=" + userId); + } + + final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId); + if (!overlayInfo.isMutable) { + // Ignore immutable overlays. + throw new OperationFailedException( + "cannot change priority of an immutable overlay package at runtime"); + } + + if (mSettings.setHighestPriority(overlay, userId)) { + return Set.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); + } + return Set.of(); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); } + } + + Optional<PackageAndUser> setLowestPriority(@NonNull final OverlayIdentifier overlay, + final int userId) throws OperationFailedException { + try{ + if (DEBUG) { + Slog.d(TAG, "setLowestPriority packageName=" + overlay + " userId=" + userId); + } + + final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId); + if (!overlayInfo.isMutable) { + // Ignore immutable overlays. + throw new OperationFailedException( + "cannot change priority of an immutable overlay package at runtime"); + } - if (mSettings.setLowestPriority(packageName, userId)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); + if (mSettings.setLowestPriority(overlay, userId)) { + return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); + } + return Optional.empty(); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); } - return Optional.empty(); } void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { @@ -693,9 +688,14 @@ final class OverlayManagerServiceImpl { return mDefaultOverlays; } - void removeIdmapForOverlay(String packageName, int userId) { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - removeIdmapIfPossible(oi); + void removeIdmapForOverlay(OverlayIdentifier overlay, int userId) + throws OperationFailedException { + try { + final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId); + removeIdmapIfPossible(oi); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); + } } OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName, @@ -709,7 +709,11 @@ final class OverlayManagerServiceImpl { if (!oi.isEnabled()) { continue; } - paths.addApkPath(oi.baseCodePath); + if (oi.isFabricated()) { + paths.addNonApkPath(oi.baseCodePath); + } else { + paths.addApkPath(oi.baseCodePath); + } } return paths.build(); } @@ -717,49 +721,53 @@ final class OverlayManagerServiceImpl { /** * Returns true if the settings/state was modified, false otherwise. */ - private boolean updateState(@NonNull final String targetPackageName, - @NonNull final String overlayPackageName, final int userId, final int flags) - throws OverlayManagerSettings.BadKeyException { - - final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId); - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName, - userId); + private boolean updateState(@NonNull final CriticalOverlayInfo info, + final int userId, final int flags) throws OverlayManagerSettings.BadKeyException { + final OverlayIdentifier overlay = info.getOverlayIdentifier(); + final AndroidPackage targetPackage = mPackageManager.getPackageForUser( + info.getTargetPackageName(), userId); + final AndroidPackage overlayPackage = mPackageManager.getPackageForUser( + info.getPackageName(), userId); - // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native - // layers. boolean modified = false; - if (targetPackage != null && overlayPackage != null - && !("android".equals(targetPackageName) - && !isPackageConfiguredMutable(overlayPackageName))) { - modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); + if (overlayPackage == null) { + removeIdmapIfPossible(mSettings.getOverlayInfo(overlay, userId)); + return mSettings.remove(overlay, userId); } - if (overlayPackage != null) { - modified |= mSettings.setBaseCodePath(overlayPackageName, userId, - overlayPackage.applicationInfo.getBaseCodePath()); - modified |= mSettings.setCategory(overlayPackageName, userId, - overlayPackage.overlayCategory); + modified |= mSettings.setCategory(overlay, userId, overlayPackage.getOverlayCategory()); + if (!info.isFabricated()) { + modified |= mSettings.setBaseCodePath(overlay, userId, overlayPackage.getBaseApkPath()); } - final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId); - final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage, + // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native + // layers. + final OverlayInfo updatedOverlayInfo = mSettings.getOverlayInfo(overlay, userId); + if (targetPackage != null && !("android".equals(info.getTargetPackageName()) + && !isPackageConfiguredMutable(overlayPackage))) { + modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage, + updatedOverlayInfo.baseCodePath, overlay.getOverlayName(), userId); + } + + final @OverlayInfo.State int currentState = mSettings.getState(overlay, userId); + final @OverlayInfo.State int newState = calculateNewState(updatedOverlayInfo, targetPackage, userId, flags); if (currentState != newState) { if (DEBUG) { Slog.d(TAG, String.format("%s:%d: %s -> %s", - overlayPackageName, userId, + overlay, userId, OverlayInfo.stateToString(currentState), OverlayInfo.stateToString(newState))); } - modified |= mSettings.setState(overlayPackageName, userId, newState); + modified |= mSettings.setState(overlay, userId, newState); } + return modified; } - private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage, - @Nullable final PackageInfo overlayPackage, final int userId, final int flags) + private @OverlayInfo.State int calculateNewState(@NonNull final OverlayInfo info, + @Nullable final AndroidPackage targetPackage, final int userId, final int flags) throws OverlayManagerSettings.BadKeyException { - if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) { return STATE_TARGET_IS_BEING_REPLACED; } @@ -768,20 +776,15 @@ final class OverlayManagerServiceImpl { return STATE_OVERLAY_IS_BEING_REPLACED; } - // assert expectation on overlay package: can only be null if the flags are used - if (DEBUG && overlayPackage == null) { - throw new IllegalArgumentException("null overlay package not compatible with no flags"); - } - if (targetPackage == null) { return STATE_MISSING_TARGET; } - if (!mIdmapManager.idmapExists(overlayPackage, userId)) { + if (!mIdmapManager.idmapExists(info)) { return STATE_NO_IDMAP; } - final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId); + final boolean enabled = mSettings.getEnabled(info.getOverlayIdentifier(), userId); return enabled ? STATE_ENABLED : STATE_DISABLED; } @@ -810,7 +813,7 @@ final class OverlayManagerServiceImpl { final int[] userIds = mSettings.getUsers(); for (int userId : userIds) { try { - final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId); + final OverlayInfo tmp = mSettings.getOverlayInfo(oi.getOverlayIdentifier(), userId); if (tmp != null && tmp.isEnabled()) { // someone is still using the idmap file -> we cannot remove it return; diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index 0613dff31da5..e3e090663a89 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -21,31 +21,32 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; +import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -68,34 +69,45 @@ final class OverlayManagerSettings { */ private final ArrayList<SettingsItem> mItems = new ArrayList<>(); - void init(@NonNull final String packageName, final int userId, + @NonNull + OverlayInfo init(@NonNull final OverlayIdentifier overlay, final int userId, @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority, - @Nullable String overlayCategory) { - remove(packageName, userId); - insert(new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, - baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, isMutable, priority, - overlayCategory)); + @Nullable String overlayCategory, boolean isFabricated) { + remove(overlay, userId); + final SettingsItem item = new SettingsItem(overlay, userId, targetPackageName, + targetOverlayableName, baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, + isMutable, priority, overlayCategory, isFabricated); + insert(item); + return item.getOverlayInfo(); } /** * Returns true if the settings were modified, false if they remain the same. */ - boolean remove(@NonNull final String packageName, final int userId) { - final int idx = select(packageName, userId); + boolean remove(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); if (idx < 0) { return false; } - mItems.remove(idx); return true; } - @NonNull OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) + @NonNull OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); + } + return mItems.get(idx).getOverlayInfo(); + } + + @Nullable + OverlayInfo getNullableOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); + if (idx < 0) { + return null; } return mItems.get(idx).getOverlayInfo(); } @@ -103,28 +115,29 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setBaseCodePath(@NonNull final String packageName, final int userId, + boolean setBaseCodePath(@NonNull final OverlayIdentifier overlay, final int userId, @NonNull final String path) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setBaseCodePath(path); } - boolean setCategory(@NonNull final String packageName, final int userId, + boolean setCategory(@NonNull final OverlayIdentifier overlay, final int userId, @Nullable String category) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setCategory(category); } - boolean getEnabled(@NonNull final String packageName, final int userId) throws BadKeyException { - final int idx = select(packageName, userId); + boolean getEnabled(@NonNull final OverlayIdentifier overlay, final int userId) + throws BadKeyException { + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).isEnabled(); } @@ -132,20 +145,20 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setEnabled(@NonNull final String packageName, final int userId, final boolean enable) - throws BadKeyException { - final int idx = select(packageName, userId); + boolean setEnabled(@NonNull final OverlayIdentifier overlay, final int userId, + final boolean enable) throws BadKeyException { + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setEnabled(enable); } - @OverlayInfo.State int getState(@NonNull final String packageName, final int userId) + @OverlayInfo.State int getState(@NonNull final OverlayIdentifier overlay, final int userId) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).getState(); } @@ -153,11 +166,11 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setState(@NonNull final String packageName, final int userId, + boolean setState(@NonNull final OverlayIdentifier overlay, final int userId, final @OverlayInfo.State int state) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setState(state); } @@ -166,53 +179,82 @@ final class OverlayManagerSettings { final int userId) { // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be // ignored in OverlayManagerService. - return selectWhereTarget(targetPackageName, userId) - .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName())) - .map(SettingsItem::getOverlayInfo) - .collect(Collectors.toList()); + final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId); + items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay); + return CollectionUtils.map(items, SettingsItem::getOverlayInfo); } ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be // ignored in OverlayManagerService. - return selectWhereUser(userId) - .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName())) - .map(SettingsItem::getOverlayInfo) - .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new, - Collectors.toList())); + final List<SettingsItem> items = selectWhereUser(userId); + items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay); + + final ArrayMap<String, List<OverlayInfo>> targetInfos = new ArrayMap<>(); + for (int i = 0, n = items.size(); i < n; i++) { + final SettingsItem item = items.get(i); + targetInfos.computeIfAbsent(item.mTargetPackageName, (String) -> new ArrayList<>()) + .add(item.getOverlayInfo()); + } + return targetInfos; + } + + Set<String> getAllBaseCodePaths() { + final Set<String> paths = new ArraySet<>(); + mItems.forEach(item -> paths.add(item.mBaseCodePath)); + return paths; + } + + @NonNull + List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) { + return removeIf(info -> (predicate.test(info) && info.userId == userId)); + } + + @NonNull + List<OverlayInfo> removeIf(final @NonNull Predicate<OverlayInfo> predicate) { + List<OverlayInfo> removed = null; + for (int i = mItems.size() - 1; i >= 0; i--) { + final OverlayInfo info = mItems.get(i).getOverlayInfo(); + if (predicate.test(info)) { + mItems.remove(i); + removed = CollectionUtils.add(removed, info); + } + } + return CollectionUtils.emptyIfNull(removed); } int[] getUsers() { return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray(); } + private static boolean isImmutableFrameworkOverlay(@NonNull SettingsItem item) { + return !item.isMutable() && "android".equals(item.getTargetPackageName()); + } + /** * Returns true if the settings were modified, false if they remain the same. */ boolean removeUser(final int userId) { - boolean removed = false; - for (int i = 0; i < mItems.size(); i++) { - final SettingsItem item = mItems.get(i); + return mItems.removeIf(item -> { if (item.getUserId() == userId) { if (DEBUG) { - Slog.d(TAG, "Removing overlay " + item.mPackageName + " for user " + userId + Slog.d(TAG, "Removing overlay " + item.mOverlay + " for user " + userId + " from settings because user was removed"); } - mItems.remove(i); - removed = true; - i--; + return true; } - } - return removed; + return false; + }); } /** * Reassigns the priority of an overlay maintaining the values of the overlays other settings. */ - void setPriority(@NonNull final String packageName, final int userId, final int priority) { - final int moveIdx = select(packageName, userId); + void setPriority(@NonNull final OverlayIdentifier overlay, final int userId, + final int priority) throws BadKeyException { + final int moveIdx = select(overlay, userId); if (moveIdx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } final SettingsItem itemToMove = mItems.get(moveIdx); @@ -224,17 +266,17 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setPriority(@NonNull final String packageName, - @NonNull final String newParentPackageName, final int userId) { - if (packageName.equals(newParentPackageName)) { + boolean setPriority(@NonNull final OverlayIdentifier overlay, + @NonNull final OverlayIdentifier newOverlay, final int userId) { + if (overlay.equals(newOverlay)) { return false; } - final int moveIdx = select(packageName, userId); + final int moveIdx = select(overlay, userId); if (moveIdx < 0) { return false; } - final int parentIdx = select(newParentPackageName, userId); + final int parentIdx = select(newOverlay, userId); if (parentIdx < 0) { return false; } @@ -248,7 +290,7 @@ final class OverlayManagerSettings { } mItems.remove(moveIdx); - final int newParentIdx = select(newParentPackageName, userId) + 1; + final int newParentIdx = select(newOverlay, userId) + 1; mItems.add(newParentIdx, itemToMove); return moveIdx != newParentIdx; } @@ -256,8 +298,8 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setLowestPriority(@NonNull final String packageName, final int userId) { - final int idx = select(packageName, userId); + boolean setLowestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); if (idx <= 0) { // If the item doesn't exist or is already the lowest, don't change anything. return false; @@ -272,8 +314,8 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setHighestPriority(@NonNull final String packageName, final int userId) { - final int idx = select(packageName, userId); + boolean setHighestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); // If the item doesn't exist or is already the highest, don't change anything. if (idx < 0 || idx == mItems.size() - 1) { @@ -297,7 +339,6 @@ final class OverlayManagerSettings { break; } } - mItems.add(i + 1, item); } @@ -308,7 +349,12 @@ final class OverlayManagerSettings { items = items.filter(item -> item.mUserId == dumpState.getUserId()); } if (dumpState.getPackageName() != null) { - items = items.filter(item -> item.mPackageName.equals(dumpState.getPackageName())); + items = items.filter(item -> item.mOverlay.getPackageName() + .equals(dumpState.getPackageName())); + } + if (dumpState.getOverlayName() != null) { + items = items.filter(item -> item.mOverlay.getOverlayName() + .equals(dumpState.getOverlayName())); } // display items @@ -322,10 +368,11 @@ final class OverlayManagerSettings { private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw, @NonNull final SettingsItem item) { - pw.println(item.mPackageName + ":" + item.getUserId() + " {"); + pw.println(item.mOverlay + ":" + item.getUserId() + " {"); pw.increaseIndent(); - pw.println("mPackageName...........: " + item.mPackageName); + pw.println("mPackageName...........: " + item.mOverlay.getPackageName()); + pw.println("mOverlayName...........: " + item.mOverlay.getOverlayName()); pw.println("mUserId................: " + item.getUserId()); pw.println("mTargetPackageName.....: " + item.getTargetPackageName()); pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName()); @@ -335,6 +382,7 @@ final class OverlayManagerSettings { pw.println("mIsMutable.............: " + item.isMutable()); pw.println("mPriority..............: " + item.mPriority); pw.println("mCategory..............: " + item.mCategory); + pw.println("mIsFabricated..........: " + item.mIsFabricated); pw.decreaseIndent(); pw.println("}"); @@ -344,7 +392,10 @@ final class OverlayManagerSettings { @NonNull final SettingsItem item, @NonNull final String field) { switch (field) { case "packagename": - pw.println(item.mPackageName); + pw.println(item.mOverlay.getPackageName()); + break; + case "overlayname": + pw.println(item.mOverlay.getOverlayName()); break; case "userid": pw.println(item.mUserId); @@ -392,6 +443,7 @@ final class OverlayManagerSettings { private static final String ATTR_BASE_CODE_PATH = "baseCodePath"; private static final String ATTR_IS_ENABLED = "isEnabled"; private static final String ATTR_PACKAGE_NAME = "packageName"; + private static final String ATTR_OVERLAY_NAME = "overlayName"; private static final String ATTR_STATE = "state"; private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName"; private static final String ATTR_TARGET_OVERLAYABLE_NAME = "targetOverlayableName"; @@ -400,30 +452,26 @@ final class OverlayManagerSettings { private static final String ATTR_CATEGORY = "category"; private static final String ATTR_USER_ID = "userId"; private static final String ATTR_VERSION = "version"; + private static final String ATTR_IS_FABRICATED = "fabricated"; @VisibleForTesting static final int CURRENT_VERSION = 4; public static void restore(@NonNull final ArrayList<SettingsItem> table, @NonNull final InputStream is) throws IOException, XmlPullParserException { + table.clear(); + final TypedXmlPullParser parser = Xml.resolvePullParser(is); + XmlUtils.beginDocument(parser, TAG_OVERLAYS); + final int version = parser.getAttributeInt(null, ATTR_VERSION); + if (version != CURRENT_VERSION) { + upgrade(version); + } - { - table.clear(); - final TypedXmlPullParser parser = Xml.resolvePullParser(is); - XmlUtils.beginDocument(parser, TAG_OVERLAYS); - int version = parser.getAttributeInt(null, ATTR_VERSION); - if (version != CURRENT_VERSION) { - upgrade(version); - } - int depth = parser.getDepth(); - - while (XmlUtils.nextElementWithin(parser, depth)) { - switch (parser.getName()) { - case TAG_ITEM: - final SettingsItem item = restoreRow(parser, depth + 1); - table.add(item); - break; - } + final int depth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, depth)) { + if (TAG_ITEM.equals(parser.getName())) { + final SettingsItem item = restoreRow(parser, depth + 1); + table.add(item); } } } @@ -447,7 +495,9 @@ final class OverlayManagerSettings { private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser, final int depth) throws IOException, XmlPullParserException { - final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME); + final OverlayIdentifier overlay = new OverlayIdentifier( + XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME), + XmlUtils.readStringAttribute(parser, ATTR_OVERLAY_NAME)); final int userId = parser.getAttributeInt(null, ATTR_USER_ID); final String targetPackageName = XmlUtils.readStringAttribute(parser, ATTR_TARGET_PACKAGE_NAME); @@ -459,9 +509,11 @@ final class OverlayManagerSettings { final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false); final int priority = parser.getAttributeInt(null, ATTR_PRIORITY); final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); + final boolean isFabricated = parser.getAttributeBoolean(null, ATTR_IS_FABRICATED, + false); - return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, - baseCodePath, state, isEnabled, !isStatic, priority, category); + return new SettingsItem(overlay, userId, targetPackageName, targetOverlayableName, + baseCodePath, state, isEnabled, !isStatic, priority, category, isFabricated); } public static void persist(@NonNull final ArrayList<SettingsItem> table, @@ -484,7 +536,8 @@ final class OverlayManagerSettings { private static void persistRow(@NonNull final TypedXmlSerializer xml, @NonNull final SettingsItem item) throws IOException { xml.startTag(null, TAG_ITEM); - XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName); + XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mOverlay.getPackageName()); + XmlUtils.writeStringAttribute(xml, ATTR_OVERLAY_NAME, item.mOverlay.getOverlayName()); xml.attributeInt(null, ATTR_USER_ID, item.mUserId); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME, @@ -495,13 +548,14 @@ final class OverlayManagerSettings { XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable); xml.attributeInt(null, ATTR_PRIORITY, item.mPriority); XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory); + XmlUtils.writeBooleanAttribute(xml, ATTR_IS_FABRICATED, item.mIsFabricated); xml.endTag(null, TAG_ITEM); } } private static final class SettingsItem { private final int mUserId; - private final String mPackageName; + private final OverlayIdentifier mOverlay; private final String mTargetPackageName; private final String mTargetOverlayableName; private String mBaseCodePath; @@ -511,13 +565,15 @@ final class OverlayManagerSettings { private boolean mIsMutable; private int mPriority; private String mCategory; + private boolean mIsFabricated; - SettingsItem(@NonNull final String packageName, final int userId, + SettingsItem(@NonNull final OverlayIdentifier overlay, final int userId, @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, final @OverlayInfo.State int state, final boolean isEnabled, - final boolean isMutable, final int priority, @Nullable String category) { - mPackageName = packageName; + final boolean isMutable, final int priority, @Nullable String category, + final boolean isFabricated) { + mOverlay = overlay; mUserId = userId; mTargetPackageName = targetPackageName; mTargetOverlayableName = targetOverlayableName; @@ -528,6 +584,7 @@ final class OverlayManagerSettings { mCache = null; mIsMutable = isMutable; mPriority = priority; + mIsFabricated = isFabricated; } private String getTargetPackageName() { @@ -596,8 +653,9 @@ final class OverlayManagerSettings { private OverlayInfo getOverlayInfo() { if (mCache == null) { - mCache = new OverlayInfo(mPackageName, mTargetPackageName, mTargetOverlayableName, - mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsMutable); + mCache = new OverlayInfo(mOverlay.getPackageName(), mOverlay.getOverlayName(), + mTargetPackageName, mTargetOverlayableName, mCategory, mBaseCodePath, + mState, mUserId, mPriority, mIsMutable, mIsFabricated); } return mCache; } @@ -620,30 +678,40 @@ final class OverlayManagerSettings { } } - private int select(@NonNull final String packageName, final int userId) { + private int select(@NonNull final OverlayIdentifier overlay, final int userId) { final int n = mItems.size(); for (int i = 0; i < n; i++) { final SettingsItem item = mItems.get(i); - if (item.mUserId == userId && item.mPackageName.equals(packageName)) { + if (item.mUserId == userId && item.mOverlay.equals(overlay)) { return i; } } return -1; } - private Stream<SettingsItem> selectWhereUser(final int userId) { - return mItems.stream().filter(item -> item.mUserId == userId); + private List<SettingsItem> selectWhereUser(final int userId) { + final List<SettingsItem> selectedItems = new ArrayList<>(); + CollectionUtils.addIf(mItems, selectedItems, i -> i.mUserId == userId); + return selectedItems; + } + + private List<SettingsItem> selectWhereOverlay(@NonNull final String packageName, + final int userId) { + final List<SettingsItem> items = selectWhereUser(userId); + items.removeIf(i -> !i.mOverlay.getPackageName().equals(packageName)); + return items; } - private Stream<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName, + private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName, final int userId) { - return selectWhereUser(userId) - .filter(item -> item.getTargetPackageName().equals(targetPackageName)); + final List<SettingsItem> items = selectWhereUser(userId); + items.removeIf(i -> !i.getTargetPackageName().equals(targetPackageName)); + return items; } - static final class BadKeyException extends RuntimeException { - BadKeyException(@NonNull final String packageName, final int userId) { - super("Bad key mPackageName=" + packageName + " mUserId=" + userId); + static final class BadKeyException extends Exception { + BadKeyException(@NonNull final OverlayIdentifier overlay, final int userId) { + super("Bad key '" + overlay + "' for user " + userId ); } } } diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index bf99bd6248cd..b7b72d11a264 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -19,20 +19,28 @@ package com.android.server.om; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.om.FabricatedOverlay; import android.content.om.IOverlayManager; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; +import android.content.om.OverlayManagerTransaction; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.content.res.TypedArray; +import android.os.Binder; +import android.os.Process; import android.os.RemoteException; import android.os.ShellCommand; import android.os.UserHandle; import android.util.TypedValue; +import com.android.internal.util.ArrayUtils; + import java.io.PrintWriter; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -72,6 +80,8 @@ final class OverlayManagerShellCommand extends ShellCommand { return runSetPriority(); case "lookup": return runLookup(); + case "fabricate": + return runFabricate(); default: return handleDefaultCommands(cmd); } @@ -89,35 +99,36 @@ final class OverlayManagerShellCommand extends ShellCommand { out.println("Overlay manager (overlay) commands:"); out.println(" help"); out.println(" Print this help text."); - out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE]"); + out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE[:NAME]]"); out.println(" Print debugging information about the overlay manager."); - out.println(" With optional parameter PACKAGE, limit output to the specified"); - out.println(" package. With optional parameter FIELD, limit output to"); + out.println(" With optional parameters PACKAGE and NAME, limit output to the specified"); + out.println(" overlay or target. With optional parameter FIELD, limit output to"); out.println(" the value of that SettingsItem field. Field names are"); out.println(" case insensitive and out.println the m prefix can be omitted,"); out.println(" so the following are equivalent: mState, mstate, State, state."); - out.println(" list [--user USER_ID] [PACKAGE]"); + out.println(" list [--user USER_ID] [PACKAGE[:NAME]]"); out.println(" Print information about target and overlay packages."); out.println(" Overlay packages are printed in priority order. With optional"); - out.println(" parameter PACKAGE, limit output to the specified package."); - out.println(" enable [--user USER_ID] PACKAGE"); - out.println(" Enable overlay package PACKAGE."); - out.println(" disable [--user USER_ID] PACKAGE"); - out.println(" Disable overlay package PACKAGE."); - out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE"); - out.println(" Enable overlay package PACKAGE and disable all other overlays for"); - out.println(" its target package. If the --category option is given, only disables"); - out.println(" other overlays in the same category."); + out.println(" parameters PACKAGE and NAME, limit output to the specified overlay or"); + out.println(" target."); + out.println(" enable [--user USER_ID] PACKAGE[:NAME]"); + out.println(" Enable overlay within or owned by PACKAGE with optional unique NAME."); + out.println(" disable [--user USER_ID] PACKAGE[:NAME]"); + out.println(" Disable overlay within or owned by PACKAGE with optional unique NAME."); + out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE[:NAME]"); + out.println(" Enable overlay within or owned by PACKAGE with optional unique NAME and"); + out.println(" disable all other overlays for its target package. If the --category"); + out.println(" option is given, only disables other overlays in the same category."); out.println(" set-priority [--user USER_ID] PACKAGE PARENT|lowest|highest"); - out.println(" Change the priority of the overlay PACKAGE to be just higher than"); - out.println(" the priority of PACKAGE_PARENT If PARENT is the special keyword"); + out.println(" Change the priority of the overlay to be just higher than"); + out.println(" the priority of PARENT If PARENT is the special keyword"); out.println(" 'lowest', change priority of PACKAGE to the lowest priority."); out.println(" If PARENT is the special keyword 'highest', change priority of"); out.println(" PACKAGE to the highest priority."); out.println(" lookup [--verbose] PACKAGE-TO-LOAD PACKAGE:TYPE/NAME"); out.println(" Load a package and print the value of a given resource"); out.println(" applying the current configuration and enabled overlays."); - out.println(" For a more fine-grained alernative, use 'idmap2 lookup'."); + out.println(" For a more fine-grained alternative, use 'idmap2 lookup'."); } private int runList() throws RemoteException { @@ -192,7 +203,7 @@ final class OverlayManagerShellCommand extends ShellCommand { status = "---"; break; } - out.println(String.format("%s %s", status, oi.packageName)); + out.println(String.format("%s %s", status, oi.getOverlayIdentifier())); } private int runEnableDisable(final boolean enable) throws RemoteException { @@ -211,8 +222,88 @@ final class OverlayManagerShellCommand extends ShellCommand { } } - final String packageName = getNextArgRequired(); - return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1; + final OverlayIdentifier overlay = OverlayIdentifier.fromString(getNextArgRequired()); + mInterface.commit(new OverlayManagerTransaction.Builder() + .setEnabled(overlay, enable, userId) + .build()); + return 0; + } + + private int runFabricate() throws RemoteException { + final PrintWriter err = getErrPrintWriter(); + if (Binder.getCallingUid() != Process.ROOT_UID) { + err.println("Error: must be root to fabricate overlays through the shell"); + return 1; + } + + int userId = UserHandle.USER_SYSTEM; + String targetPackage = ""; + String targetOverlayable = ""; + String name = ""; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case "--target": + targetPackage = getNextArgRequired(); + break; + case "--target-name": + targetOverlayable = getNextArgRequired(); + break; + case "--name": + name = getNextArgRequired(); + break; + default: + err.println("Error: Unknown option: " + opt); + return 1; + } + } + + if (name.isEmpty()) { + err.println("Error: Missing required arg '--name'"); + return 1; + } + + if (targetPackage.isEmpty()) { + err.println("Error: Missing required arg '--target'"); + return 1; + } + + final String resourceName = getNextArgRequired(); + final String typeStr = getNextArgRequired(); + final int type; + if (typeStr.startsWith("0x")) { + type = Integer.parseUnsignedInt(typeStr.substring(2), 16); + } else { + type = Integer.parseUnsignedInt(typeStr); + } + final String dataStr = getNextArgRequired(); + final int data; + if (dataStr.startsWith("0x")) { + data = Integer.parseUnsignedInt(dataStr.substring(2), 16); + } else { + data = Integer.parseUnsignedInt(dataStr); + } + + final PackageManager pm = mContext.getPackageManager(); + if (pm == null) { + err.println("Error: failed to get package manager"); + return 1; + } + + final String overlayPackageName = "com.android.shell"; + final FabricatedOverlay overlay = new FabricatedOverlay.Builder( + overlayPackageName, name, targetPackage) + .setTargetOverlayable(targetOverlayable) + .setResourceValue(resourceName, type, data) + .build(); + + mInterface.commit(new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .build()); + return 0; } private int runEnableExclusive() throws RemoteException { @@ -234,12 +325,27 @@ final class OverlayManagerShellCommand extends ShellCommand { return 1; } } - final String overlay = getNextArgRequired(); - if (inCategory) { - return mInterface.setEnabledExclusiveInCategory(overlay, userId) ? 0 : 1; - } else { - return mInterface.setEnabledExclusive(overlay, true, userId) ? 0 : 1; + + final OverlayIdentifier overlay = OverlayIdentifier.fromString(getNextArgRequired()); + final OverlayInfo overlayInfo = mInterface.getOverlayInfoByIdentifier(overlay, userId); + if (overlayInfo == null) { + err.println("Error: Unable to get overlay info of: " + overlay); + return 1; + } + + final List<OverlayInfo> overlaysForTarget = + mInterface.getOverlayInfosForTarget(overlayInfo.targetPackageName, userId); + final OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder(); + for (final OverlayInfo disableOverlay : overlaysForTarget) { + if ((inCategory && !Objects.equals(disableOverlay.category,overlayInfo.category)) + || !disableOverlay.isMutable) { + continue; + } + builder.setEnabled(disableOverlay.getOverlayIdentifier(), false, userId); } + builder.setEnabled(overlayInfo.getOverlayIdentifier(), true, userId); + mInterface.commit(builder.build()); + return 0; } private int runSetPriority() throws RemoteException { diff --git a/services/core/java/com/android/server/om/PackageManagerHelper.java b/services/core/java/com/android/server/om/PackageManagerHelper.java index b1a8b4ee4d9f..750f5c3ec06b 100644 --- a/services/core/java/com/android/server/om/PackageManagerHelper.java +++ b/services/core/java/com/android/server/om/PackageManagerHelper.java @@ -22,10 +22,15 @@ import android.content.om.OverlayableInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Slog; import com.android.server.pm.PackageManagerServiceUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,6 +41,33 @@ import java.util.Map; * @hide */ interface PackageManagerHelper { + + /** + * Initializes the helper for the user. This only needs to be invoked one time before + * packages of this user are queried. + * @param userId the user id to initialize + * @return a map of package name to all packages installed in the user + */ + @NonNull + ArrayMap<String, AndroidPackage> initializeForUser(final int userId); + + /** + * Retrieves the package information if it is installed for the user. + */ + @Nullable + AndroidPackage getPackageForUser(@NonNull final String packageName, final int userId); + + /** + * Returns whether the package is an instant app. + */ + boolean isInstantApp(@NonNull final String packageName, final int userId); + + /** + * @see PackageManager#getPackagesForUid(int) + */ + @Nullable + String[] getPackagesForUid(int uid); + /** * @return true if the target package has declared an overlayable */ @@ -64,11 +96,6 @@ interface PackageManagerHelper { Map<String, Map<String, String>> getNamedActors(); /** - * @see PackageManagerInternal#getOverlayPackages(int) - */ - List<PackageInfo> getOverlayPackages(int userId); - - /** * Read from the APK and AndroidManifest of a package to return the overlayable defined for * a given name. * @@ -80,19 +107,6 @@ interface PackageManagerHelper { throws IOException; /** - * @see PackageManager#getPackagesForUid(int) - */ - @Nullable - String[] getPackagesForUid(int uid); - - /** - * @param userId user to filter package visibility by - * @see PackageManager#getPackageInfo(String, int) - */ - @Nullable - PackageInfo getPackageInfo(@NonNull String packageName, int userId); - - /** * @return true if {@link PackageManagerServiceUtils#compareSignatures} run on both packages * in the system returns {@link PackageManager#SIGNATURE_MATCH} */ diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 402f6467e82d..af0aa769f8cc 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -217,7 +217,7 @@ public class BackgroundDexOptService extends JobService { // trade-off worth doing to save boot time work. int result = pm.performDexOptWithStatus(new DexoptOptions( pkg, - PackageManagerService.REASON_BOOT, + PackageManagerService.REASON_POST_BOOT, DexoptOptions.DEXOPT_BOOT_COMPLETE)); if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { updatedPackages.add(pkg); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index f240d85572c4..e91bb46657e1 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -929,6 +929,7 @@ public class LauncherAppsService extends SystemService { // Flag for bubble to make behaviour match documentLaunchMode=always. intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT); intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + intents[0].putExtra(Intent.EXTRA_IS_BUBBLED, true); } intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -1317,6 +1318,10 @@ public class LauncherAppsService extends SystemService { mListeners.finishBroadcast(); } super.onPackageAdded(packageName, uid); + PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); + pmi.registerInstalledLoadingProgressCallback(packageName, + new PackageLoadingProgressCallback(packageName, user), + user.getIdentifier()); } @Override @@ -1538,5 +1543,38 @@ public class LauncherAppsService extends SystemService { checkCallbackCount(); } } + + class PackageLoadingProgressCallback extends + PackageManagerInternal.InstalledLoadingProgressCallback { + private String mPackageName; + private UserHandle mUser; + + PackageLoadingProgressCallback(String packageName, UserHandle user) { + super(mCallbackHandler); + mPackageName = packageName; + mUser = user; + } + + @Override + public void onLoadingProgressChanged(float progress) { + final int n = mListeners.beginBroadcast(); + try { + for (int i = 0; i < n; i++) { + IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); + if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) { + continue; + } + try { + listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress); + } catch (RemoteException re) { + Slog.d(TAG, "Callback failed ", re); + } + } + } finally { + mListeners.finishBroadcast(); + } + } + } } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index fc02b3439d16..b9e3e0f4450b 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -687,7 +687,8 @@ public class PackageDexOptimizer { boolean generateCompactDex = true; switch (compilationReason) { case PackageManagerService.REASON_FIRST_BOOT: - case PackageManagerService.REASON_BOOT: + case PackageManagerService.REASON_BOOT_AFTER_OTA: + case PackageManagerService.REASON_POST_BOOT: case PackageManagerService.REASON_INSTALL: generateCompactDex = false; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0c143c97e1aa..7bf66c5541a3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -780,17 +780,18 @@ public class PackageManagerService extends IPackageManager.Stub // Compilation reasons. public static final int REASON_UNKNOWN = -1; public static final int REASON_FIRST_BOOT = 0; - public static final int REASON_BOOT = 1; - public static final int REASON_INSTALL = 2; - public static final int REASON_INSTALL_FAST = 3; - public static final int REASON_INSTALL_BULK = 4; - public static final int REASON_INSTALL_BULK_SECONDARY = 5; - public static final int REASON_INSTALL_BULK_DOWNGRADED = 6; - public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 7; - public static final int REASON_BACKGROUND_DEXOPT = 8; - public static final int REASON_AB_OTA = 9; - public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 10; - public static final int REASON_SHARED = 11; + public static final int REASON_BOOT_AFTER_OTA = 1; + public static final int REASON_POST_BOOT = 2; + public static final int REASON_INSTALL = 3; + public static final int REASON_INSTALL_FAST = 4; + public static final int REASON_INSTALL_BULK = 5; + public static final int REASON_INSTALL_BULK_SECONDARY = 6; + public static final int REASON_INSTALL_BULK_DOWNGRADED = 7; + public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 8; + public static final int REASON_BACKGROUND_DEXOPT = 9; + public static final int REASON_AB_OTA = 10; + public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 11; + public static final int REASON_SHARED = 12; public static final int REASON_LAST = REASON_SHARED; @@ -11637,10 +11638,7 @@ public class PackageManagerService extends IPackageManager.Stub // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; - // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. - boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); - - if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { + if (!causeUpgrade && !causeFirstBoot) { return; } @@ -11657,7 +11655,7 @@ public class PackageManagerService extends IPackageManager.Stub final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, - causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, + causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA, false /* bootComplete */); final int elapsedTimeSeconds = @@ -25949,6 +25947,13 @@ public class PackageManagerService extends IPackageManager.Stub public String getModuleMetadataPackageName() throws RemoteException { return PackageManagerService.this.mModuleInfoProvider.getPackageName(); } + + @Override + public boolean hasSha256SigningCertificate(String packageName, byte[] certificate) + throws RemoteException { + return PackageManagerService.this.hasSigningCertificate( + packageName, certificate, CERT_INPUT_SHA256); + } } private AndroidPackage getPackage(String packageName) { @@ -27029,6 +27034,28 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public boolean registerInstalledLoadingProgressCallback(String packageName, + PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) { + final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(), + userId); + if (ps == null) { + return false; + } + if (!ps.isPackageLoading()) { + Slog.w(TAG, + "Failed registering loading progress callback. Package is fully loaded."); + return false; + } + if (mIncrementalManager == null) { + Slog.w(TAG, + "Failed registering loading progress callback. Incremental is not enabled"); + return false; + } + return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(), + (IPackageLoadingProgressCallback) callback.getBinder()); + } + + @Override public IncrementalStatesInfo getIncrementalStatesInfo( @NonNull String packageName, int filterCallingUid, int userId) { final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid, diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java index 9cd55a6bb07e..636db111be88 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java @@ -29,7 +29,8 @@ public class PackageManagerServiceCompilerMapping { // Names for compilation reasons. public static final String REASON_STRINGS[] = { "first-boot", - "boot", + "boot-after-ota", + "post-boot", "install", "install-fast", "install-bulk", diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 139654ef4561..3576950bd3a6 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -587,7 +587,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { private static final int TRON_COMPILATION_REASON_ERROR = 0; private static final int TRON_COMPILATION_REASON_UNKNOWN = 1; private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2; - private static final int TRON_COMPILATION_REASON_BOOT = 3; + private static final int TRON_COMPILATION_REASON_BOOT_DEPRECATED_SINCE_S = 3; private static final int TRON_COMPILATION_REASON_INSTALL = 4; private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5; private static final int TRON_COMPILATION_REASON_AB_OTA = 6; @@ -605,6 +605,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { private static final int TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED_WITH_DM = 18; private static final int TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED_WITH_DM = 19; + private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20; + private static final int TRON_COMPILATION_REASON_POST_BOOT = 21; // The annotation to add as a suffix to the compilation reason when dexopt was // performed with dex metadata. @@ -618,7 +620,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN; case "error" : return TRON_COMPILATION_REASON_ERROR; case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT; - case "boot" : return TRON_COMPILATION_REASON_BOOT; + case "boot-after-ota": return TRON_COMPILATION_REASON_BOOT_AFTER_OTA; + case "post-boot" : return TRON_COMPILATION_REASON_POST_BOOT; case "install" : return TRON_COMPILATION_REASON_INSTALL; case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT; case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA; diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java index 30c334d22b6a..32bee5809b11 100644 --- a/services/core/java/com/android/server/pm/permission/Permission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -38,6 +38,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collection; import java.util.Objects; +import java.util.Set; /** * Permission definition. @@ -345,6 +346,14 @@ public final class Permission { return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_ROLE) != 0; } + public boolean isKnownSigner() { + return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0; + } + + public Set<String> getKnownCerts() { + return mPermissionInfo.knownCerts; + } + public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) { if (!oldPackageName.equals(mPermissionInfo.packageName)) { return; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index aff871118a34..e486f087535b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2590,6 +2590,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean runtimePermissionsRevoked = false; int[] updatedUserIds = EMPTY_INT_ARRAY; + ArraySet<String> isPrivilegedPermissionAllowlisted = null; ArraySet<String> shouldGrantSignaturePermission = null; ArraySet<String> shouldGrantInternalPermission = null; final List<String> requestedPermissions = pkg.getRequestedPermissions(); @@ -2604,7 +2605,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (permission == null) { continue; } - if (permission.isSignature() && (shouldGrantSignaturePermission(pkg, permission) + if (permission.isPrivileged() + && checkPrivilegedPermissionAllowlist(pkg, ps, permission)) { + if (isPrivilegedPermissionAllowlisted == null) { + isPrivilegedPermissionAllowlisted = new ArraySet<>(); + } + isPrivilegedPermissionAllowlisted.add(permissionName); + } + if (permission.isSignature() && (shouldGrantPermissionBySignature(pkg, permission) || shouldGrantPermissionByProtectionFlags(pkg, ps, permission))) { if (shouldGrantSignaturePermission == null) { shouldGrantSignaturePermission = new ArraySet<>(); @@ -2830,13 +2838,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { if ((bp.isNormal() && shouldGrantNormalPermission) || (bp.isSignature() - && ((shouldGrantSignaturePermission != null - && shouldGrantSignaturePermission.contains(permName)) + && (!bp.isPrivileged() || CollectionUtils.contains( + isPrivilegedPermissionAllowlisted, permName)) + && (CollectionUtils.contains(shouldGrantSignaturePermission, + permName) || ((bp.isDevelopment() || bp.isRole()) && origState.isPermissionGranted(permName)))) || (bp.isInternal() - && ((shouldGrantInternalPermission != null - && shouldGrantInternalPermission.contains(permName)) + && (!bp.isPrivileged() || CollectionUtils.contains( + isPrivilegedPermissionAllowlisted, permName)) + && (CollectionUtils.contains(shouldGrantInternalPermission, + permName) || ((bp.isDevelopment() || bp.isRole()) && origState.isPermissionGranted(permName))))) { // Grant an install permission. @@ -3343,7 +3355,92 @@ public class PermissionManagerService extends IPermissionManager.Stub { return allowed; } - private boolean shouldGrantSignaturePermission(@NonNull AndroidPackage pkg, + private boolean checkPrivilegedPermissionAllowlist(@NonNull AndroidPackage pkg, + @NonNull PackageSetting packageSetting, @NonNull Permission permission) { + if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) { + return true; + } + final String packageName = pkg.getPackageName(); + if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) { + return true; + } + if (!pkg.isPrivileged()) { + return true; + } + if (!Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)) { + return true; + } + final String permissionName = permission.getName(); + if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) { + return true; + } + // Only enforce the allowlist on boot + if (!mSystemReady + // Updated system apps do not need to be allowlisted + && !packageSetting.getPkgState().isUpdatedSystemApp()) { + final ApexManager apexManager = ApexManager.getInstance(); + final String containingApexPackageName = + apexManager.getActiveApexPackageNameContainingPackage(packageName); + final boolean isInUpdatedApex = containingApexPackageName != null + && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName, + MATCH_ACTIVE_PACKAGE)); + // Apps that are in updated apexs' do not need to be allowlisted + if (!isInUpdatedApex) { + // it's only a reportable violation if the permission isn't explicitly + // denied + if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) { + return false; + } + Slog.w(TAG, "Privileged permission " + permissionName + " for package " + + packageName + " (" + pkg.getPath() + + ") not in privapp-permissions allowlist"); + if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) { + synchronized (mLock) { + if (mPrivappPermissionsViolations == null) { + mPrivappPermissionsViolations = new ArraySet<>(); + } + mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): " + + permissionName); + } + } + } + } + return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE; + } + + private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg, + @NonNull String permission) { + final SystemConfig systemConfig = SystemConfig.getInstance(); + final Set<String> permissions; + if (pkg.isVendor()) { + permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName()); + } else if (pkg.isProduct()) { + permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName()); + } else if (pkg.isSystemExt()) { + permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName()); + } else { + permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName()); + } + return CollectionUtils.contains(permissions, permission); + } + + private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg, + @NonNull String permission) { + final SystemConfig systemConfig = SystemConfig.getInstance(); + final Set<String> permissions; + if (pkg.isVendor()) { + permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName()); + } else if (pkg.isProduct()) { + permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName()); + } else if (pkg.isSystemExt()) { + permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName()); + } else { + permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName()); + } + return CollectionUtils.contains(permissions, permission); + } + + private boolean shouldGrantPermissionBySignature(@NonNull AndroidPackage pkg, @NonNull Permission bp) { // expect single system package String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames( @@ -3373,8 +3470,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg, @NonNull PackageSetting pkgSetting, @NonNull Permission bp) { boolean allowed = false; - final boolean isVendorPrivilegedPermission = bp.isVendorPrivileged(); - final boolean isPrivilegedPermission = bp.isPrivileged() || isVendorPrivilegedPermission; + final boolean isPrivilegedPermission = bp.isPrivileged(); final boolean isOemPermission = bp.isOem(); if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) { final String permissionName = bp.getName(); @@ -3386,19 +3482,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg; if (disabledPkg != null && disabledPkg.getRequestedPermissions().contains( permissionName)) { - allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(disabledPkg, - true, bp)) || (isOemPermission && canGrantOemPermission(disabledPkg, + allowed = (isPrivilegedPermission && disabledPkg.isPrivileged()) + || (isOemPermission && canGrantOemPermission(disabledPkg, permissionName)); } } else { - allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(pkg, false, bp)) + allowed = (isPrivilegedPermission && pkg.isPrivileged()) || (isOemPermission && canGrantOemPermission(pkg, permissionName)); } // In any case, don't grant a privileged permission to privileged vendor apps, if // the permission's protectionLevel does not have the extra 'vendorPrivileged' // flag. - if (allowed && isPrivilegedPermission && !isVendorPrivilegedPermission - && pkg.isVendor()) { + if (allowed && isPrivilegedPermission && !bp.isVendorPrivileged() && pkg.isVendor()) { Slog.w(TAG, "Permission " + permissionName + " cannot be granted to privileged vendor apk " + pkg.getPackageName() + " because it isn't a 'vendorPrivileged' permission."); @@ -3438,6 +3533,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Any pre-installed system app is allowed to get this permission. allowed = true; } + if (!allowed && bp.isKnownSigner()) { + // If the permission is to be granted to a known signer then check if any of this + // app's signing certificates are in the trusted certificate digest Set. + allowed = pkg.getSigningDetails().hasAncestorOrSelfWithDigest(bp.getKnownCerts()); + } // Deferred to be checked under permission data lock inside restorePermissionState(). //if (!allowed && bp.isDevelopment()) { // // For development permissions, a development permission @@ -3536,90 +3636,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return mPackageManagerInt.getPackageSetting(sourcePackageName); } - private boolean canGrantPrivilegedPermission(@NonNull AndroidPackage pkg, - boolean isUpdatedSystemApp, @NonNull Permission permission) { - if (!pkg.isPrivileged()) { - return false; - } - final boolean isPlatformPermission = PLATFORM_PACKAGE_NAME.equals( - permission.getPackageName()); - if (!isPlatformPermission) { - return true; - } - if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) { - return true; - } - final String permissionName = permission.getName(); - if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) { - return true; - } - // Only enforce the allowlist on boot - if (!mSystemReady - // Updated system apps do not need to be allowlisted - && !isUpdatedSystemApp) { - final ApexManager apexManager = ApexManager.getInstance(); - final String packageName = pkg.getPackageName(); - final String containingApexPackageName = - apexManager.getActiveApexPackageNameContainingPackage(packageName); - final boolean isInUpdatedApex = containingApexPackageName != null - && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName, - MATCH_ACTIVE_PACKAGE)); - // Apps that are in updated apexs' do not need to be allowlisted - if (!isInUpdatedApex) { - // it's only a reportable violation if the permission isn't explicitly - // denied - if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) { - return false; - } - Slog.w(TAG, "Privileged permission " + permissionName + " for package " - + packageName + " (" + pkg.getPath() - + ") not in privapp-permissions allowlist"); - if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) { - synchronized (mLock) { - if (mPrivappPermissionsViolations == null) { - mPrivappPermissionsViolations = new ArraySet<>(); - } - mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): " - + permissionName); - } - } - } - } - return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE; - } - - private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg, - @NonNull String permission) { - final SystemConfig systemConfig = SystemConfig.getInstance(); - final Set<String> permissions; - if (pkg.isVendor()) { - permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName()); - } else if (pkg.isProduct()) { - permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName()); - } else if (pkg.isSystemExt()) { - permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName()); - } else { - permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName()); - } - return permissions != null && permissions.contains(permission); - } - - private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg, - @NonNull String permission) { - final SystemConfig systemConfig = SystemConfig.getInstance(); - final Set<String> permissions; - if (pkg.isVendor()) { - permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName()); - } else if (pkg.isProduct()) { - permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName()); - } else if (pkg.isSystemExt()) { - permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName()); - } else { - permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName()); - } - return permissions != null && permissions.contains(permission); - } - private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) { if (!pkg.isOem()) { return false; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index bc819617332d..6a441f1830d1 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -346,8 +346,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** Amount of time (in milliseconds) to wait for windows drawn before powering on. */ static final int WAITING_FOR_DRAWN_TIMEOUT = 1000; - /** Amount of time (in milliseconds) a toast window can be shown. */ - public static final int TOAST_WINDOW_TIMEOUT = 3500; // 3.5 seconds + /** + * Extra time for additional SystemUI animations. + * <p>Since legacy apps can add Toast windows directly instead of using Toast APIs, + * {@link DisplayPolicy} ensures that the window manager removes toast windows after + * TOAST_WINDOW_TIMEOUT. We increase this timeout by TOAST_WINDOW_ANIM_BUFFER to account for + * SystemUI's in/out toast animations, so that the toast text is still shown for a minimum + * of 3.5 seconds and the animations are finished before window manager removes the window. + */ + public static final int TOAST_WINDOW_ANIM_BUFFER = 600; + + /** + * Amount of time (in milliseconds) a toast window can be shown before it's automatically + * removed by window manager. + */ + public static final int TOAST_WINDOW_TIMEOUT = 3500 + TOAST_WINDOW_ANIM_BUFFER; /** * Lock protecting internal state. Must not call out into window diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 5e681c674d8b..539b4138cc18 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -90,6 +90,7 @@ import android.net.NetworkTemplate; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.BatteryStats; +import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -152,6 +153,7 @@ import com.android.internal.os.LooperStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.StoragedUidIoStatsReader; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.internal.util.CollectionUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.role.RoleManagerLocal; @@ -457,6 +459,8 @@ public class StatsPullAtomService extends SystemService { synchronized (mCpuTimePerUidFreqLock) { return pullCpuTimePerUidFreqLocked(atomTag, data); } + case FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER: + return pullCpuCyclesPerThreadGroupCluster(atomTag, data); case FrameworkStatsLog.CPU_ACTIVE_TIME: synchronized (mCpuActiveTimeLock) { return pullCpuActiveTimeLocked(atomTag, data); @@ -781,6 +785,7 @@ public class StatsPullAtomService extends SystemService { registerCpuTimePerUid(); registerCpuCyclesPerUidCluster(); registerCpuTimePerUidFreq(); + registerCpuCyclesPerThreadGroupCluster(); registerCpuActiveTime(); registerCpuClusterTime(); registerWifiActivityInfo(); @@ -1510,6 +1515,7 @@ public class StatsPullAtomService extends SystemService { } int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) { + // TODO(b/179485697): Remove power profile dependency. PowerProfile powerProfile = new PowerProfile(mContext); // Frequency index to frequency mapping. long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); @@ -1653,6 +1659,81 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerCpuCyclesPerThreadGroupCluster() { + // TODO(b/173227907): Register only when supported. + int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3, 4}) + .build(); + mStatsManager.setPullAtomCallback( + tagId, + metadata, + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + + int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) { + // TODO(b/179485697): Remove power profile dependency. + PowerProfile powerProfile = new PowerProfile(mContext); + // Frequency index to frequency mapping. + long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); + if (freqs == null) { + return StatsManager.PULL_SKIP; + } + // Frequency index to cluster mapping. + int[] freqClusters = new int[freqs.length]; + // Number of clusters. + int clusters; + + // Initialize frequency mappings. + { + int cluster = 0; + long lastFreq = -1; + for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex) { + long currFreq = freqs[freqIndex]; + if (currFreq <= lastFreq) { + cluster++; + } + freqClusters[freqIndex] = cluster; + lastFreq = currFreq; + } + + clusters = cluster + 1; + } + + SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class) + .getSystemServiceCpuThreadTimes(); + if (times == null) { + return StatsManager.PULL_SKIP; + } + + addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, + FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER, + times.threadCpuTimesUs, clusters, freqs, freqClusters); + addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, + FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER, + times.binderThreadCpuTimesUs, clusters, freqs, freqClusters); + + return StatsManager.PULL_SUCCESS; + } + + private static void addCpuCyclesPerThreadGroupClusterAtoms( + int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs, + int clusters, long[] freqs, int[] freqClusters) { + long[] aggregatedCycles = new long[clusters]; + long[] aggregatedTimesUs = new long[clusters]; + for (int i = 0; i < cpuTimesUs.length; ++i) { + aggregatedCycles[freqClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000; + aggregatedTimesUs[freqClusters[i]] += cpuTimesUs[i]; + } + for (int cluster = 0; cluster < clusters; ++cluster) { + pulledData.add(FrameworkStatsLog.buildStatsEvent( + atomTag, threadGroup, cluster, aggregatedCycles[cluster], + aggregatedTimesUs[cluster] / 1_000)); + } + } + private void registerCpuActiveTime() { // the throttling is 3sec, handled in // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java index a54288f73018..e463ee22452d 100644 --- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java @@ -33,9 +33,11 @@ import java.util.Objects; */ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment { - private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5); + // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented + private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1); + // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = - Duration.ofMinutes(1); + Duration.ofSeconds(20); private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5); @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal; diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 737f8107f83f..8fcdf2e96889 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -109,12 +109,13 @@ class DisplayWindowSettingsProvider implements SettingsProvider { */ void setBaseSettingsFilePath(@Nullable String path) { AtomicFile settingsFile; - if (path != null) { - settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG); + File file = path != null ? new File(path) : null; + if (file != null && file.exists()) { + settingsFile = new AtomicFile(file, WM_DISPLAY_COMMIT_TAG); } else { + Slog.w(TAG, "display settings " + path + " does not exist, using vendor defaults"); settingsFile = getVendorSettingsFile(); } - setBaseSettingsStorage(new AtomicFileStorage(settingsFile)); } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 6e8110e9c36e..d81181d46417 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -58,6 +58,8 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; @@ -1886,6 +1888,7 @@ class RecentTasks { // Fill in some deprecated values. rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; + rti.lastSnapshotData.set(tr.mLastTaskSnapshotData); // Fill in organized child task info for the task created by organizer. if (tr.mCreatedByOrganizer) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 9bbbbe0a8535..9c8a997ec098 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -158,6 +158,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData; import android.app.ActivityManager.TaskDescription; import android.app.ActivityOptions; import android.app.ActivityTaskManager; @@ -293,6 +294,9 @@ class Task extends WindowContainer<WindowContainer> { private static final String ATTR_MIN_HEIGHT = "min_height"; private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version"; private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity"; + private static final String ATTR_LAST_SNAPSHOT_TASK_SIZE = "last_snapshot_task_size"; + private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets"; + private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size"; // Set to false to disable the preview that is shown while a new activity // is being started. @@ -540,6 +544,10 @@ class Task extends WindowContainer<WindowContainer> { // NOTE: This value needs to be persisted with each task private TaskDescription mTaskDescription; + // Information about the last snapshot that should be persisted with the task to allow SystemUI + // to layout without loading all the task snapshots + final PersistedTaskSnapshotData mLastTaskSnapshotData; + // If set to true, the task will report that it is not in the floating // state regardless of it's root task affiliation. As the floating state drives // production of content insets this can be used to preserve them across @@ -613,8 +621,6 @@ class Task extends WindowContainer<WindowContainer> { SurfaceControl.Transaction mMainWindowSizeChangeTransaction; Task mMainWindowSizeChangeTask; - Rect mPreAnimationBounds = new Rect(); - private final AnimatingActivityRegistry mAnimatingActivityRegistry = new AnimatingActivityRegistry(); @@ -839,13 +845,13 @@ class Task extends WindowContainer<WindowContainer> { ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid, String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity, - TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId, - int nextTaskId, int callingUid, String callingPackage, - @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture, - boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight, - ActivityInfo info, IVoiceInteractionSession _voiceSession, - IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, - IBinder _launchCookie, boolean _deferTaskAppear) { + TaskDescription _lastTaskDescription, PersistedTaskSnapshotData _lastSnapshotData, + int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid, + String callingPackage, @Nullable String callingFeatureId, int resizeMode, + boolean supportsPictureInPicture, boolean _realActivitySuspended, + boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info, + IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, + boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear) { super(atmService.mWindowManager); mAtmService = atmService; @@ -855,7 +861,12 @@ class Task extends WindowContainer<WindowContainer> { mUserId = _userId; mResizeMode = resizeMode; mSupportsPictureInPicture = supportsPictureInPicture; - mTaskDescription = _lastTaskDescription; + mTaskDescription = _lastTaskDescription != null + ? _lastTaskDescription + : new TaskDescription(); + mLastTaskSnapshotData = _lastSnapshotData != null + ? _lastSnapshotData + : new PersistedTaskSnapshotData(); // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED). setOrientation(SCREEN_ORIENTATION_UNSET); mRemoteToken = new RemoteToken(this); @@ -3899,6 +3910,7 @@ class Task extends WindowContainer<WindowContainer> { } void onSnapshotChanged(TaskSnapshot snapshot) { + mLastTaskSnapshotData.set(snapshot); mAtmService.getTaskChangeNotificationController().notifyTaskSnapshotChanged( mTaskId, snapshot); } @@ -4157,7 +4169,6 @@ class Task extends WindowContainer<WindowContainer> { void fillTaskInfo(TaskInfo info, boolean stripExtras) { getNumRunningActivities(mReuseActivitiesReport); info.userId = isLeafTask() ? mUserId : mCurrentUser; - info.stackId = getRootTaskId(); info.taskId = mTaskId; info.displayId = getDisplayId(); info.isRunning = getTopNonFinishingActivity() != null; @@ -4707,6 +4718,19 @@ class Task extends WindowContainer<WindowContainer> { out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight); out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION); + if (mLastTaskSnapshotData.taskSize != null) { + out.attribute(null, ATTR_LAST_SNAPSHOT_TASK_SIZE, + mLastTaskSnapshotData.taskSize.flattenToString()); + } + if (mLastTaskSnapshotData.contentInsets != null) { + out.attribute(null, ATTR_LAST_SNAPSHOT_CONTENT_INSETS, + mLastTaskSnapshotData.contentInsets.flattenToString()); + } + if (mLastTaskSnapshotData.bufferSize != null) { + out.attribute(null, ATTR_LAST_SNAPSHOT_BUFFER_SIZE, + mLastTaskSnapshotData.bufferSize.flattenToString()); + } + if (affinityIntent != null) { out.startTag(null, TAG_AFFINITYINTENT); affinityIntent.saveToXml(out); @@ -4774,6 +4798,7 @@ class Task extends WindowContainer<WindowContainer> { int taskId = INVALID_TASK_ID; final int outerDepth = in.getDepth(); TaskDescription taskDescription = new TaskDescription(); + PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData(); int taskAffiliation = INVALID_TASK_ID; int prevTaskId = INVALID_TASK_ID; int nextTaskId = INVALID_TASK_ID; @@ -4883,6 +4908,15 @@ class Task extends WindowContainer<WindowContainer> { case ATTR_PERSIST_TASK_VERSION: persistTaskVersion = Integer.parseInt(attrValue); break; + case ATTR_LAST_SNAPSHOT_TASK_SIZE: + lastSnapshotData.taskSize = Point.unflattenFromString(attrValue); + break; + case ATTR_LAST_SNAPSHOT_CONTENT_INSETS: + lastSnapshotData.contentInsets = Rect.unflattenFromString(attrValue); + break; + case ATTR_LAST_SNAPSHOT_BUFFER_SIZE: + lastSnapshotData.bufferSize = Point.unflattenFromString(attrValue); + break; default: if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) { Slog.w(TAG, "Task: Unknown attribute=" + attrName); @@ -4977,6 +5011,7 @@ class Task extends WindowContainer<WindowContainer> { .setLastTimeMoved(lastTimeOnTop) .setNeverRelinquishIdentity(neverRelinquishIdentity) .setLastTaskDescription(taskDescription) + .setLastSnapshotData(lastSnapshotData) .setTaskAffiliation(taskAffiliation) .setPrevAffiliateTaskId(prevTaskId) .setNextAffiliateTaskId(nextTaskId) @@ -7904,6 +7939,7 @@ class Task extends WindowContainer<WindowContainer> { private long mLastTimeMoved; private boolean mNeverRelinquishIdentity; private TaskDescription mLastTaskDescription; + private PersistedTaskSnapshotData mLastSnapshotData; private int mTaskAffiliation; private int mPrevAffiliateTaskId = INVALID_TASK_ID; private int mNextAffiliateTaskId = INVALID_TASK_ID; @@ -8104,6 +8140,11 @@ class Task extends WindowContainer<WindowContainer> { return this; } + private Builder setLastSnapshotData(PersistedTaskSnapshotData lastSnapshotData) { + mLastSnapshotData = lastSnapshotData; + return this; + } + private Builder setOrigActivity(ComponentName origActivity) { mOrigActivity = origActivity; return this; @@ -8217,9 +8258,6 @@ class Task extends WindowContainer<WindowContainer> { mCallingPackage = mActivityInfo.packageName; mResizeMode = mActivityInfo.resizeMode; mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture(); - if (mLastTaskDescription == null) { - mLastTaskDescription = new TaskDescription(); - } final Task task = buildInner(); task.mHasBeenVisible = mHasBeenVisible; @@ -8253,9 +8291,9 @@ class Task extends WindowContainer<WindowContainer> { return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity, mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents, mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved, - mNeverRelinquishIdentity, mLastTaskDescription, mTaskAffiliation, - mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, mCallingPackage, - mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, + mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData, + mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, + mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight, mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer, mLaunchCookie, mDeferTaskAppear); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 404b0cf8c7b8..07eb7bf8f9a0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -15985,6 +15985,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public UserHandle createAndProvisionManagedProfile( @NonNull ManagedProfileProvisioningParams provisioningParams, @NonNull String callerPackage) { + Objects.requireNonNull(provisioningParams, "provisioningParams is null"); + Objects.requireNonNull(callerPackage, "callerPackage is null"); + final ComponentName admin = provisioningParams.getProfileAdminComponentName(); Objects.requireNonNull(admin, "admin is null"); @@ -15992,6 +15995,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + provisioningParams.logParams(callerPackage); + UserInfo userInfo = null; final long identity = Binder.clearCallingIdentity(); try { @@ -16291,9 +16296,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void provisionFullyManagedDevice( - FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) { - ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName(); + @NonNull FullyManagedDeviceProvisioningParams provisioningParams, + @NonNull String callerPackage) { + Objects.requireNonNull(provisioningParams, "provisioningParams is null."); + Objects.requireNonNull(callerPackage, "callerPackage is null."); + ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName(); Objects.requireNonNull(deviceAdmin, "admin is null."); Objects.requireNonNull(provisioningParams.getOwnerName(), "owner name is null."); @@ -16301,6 +16309,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + provisioningParams.logParams(callerPackage); + final long identity = Binder.clearCallingIdentity(); try { // TODO(b/178187130): This check fails silent provisioning, uncomment once silent @@ -16318,8 +16328,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime()); setLocale(provisioningParams.getLocale()); + final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode() + ? UserHandle.USER_SYSTEM : caller.getUserId(); if (!removeNonRequiredAppsForManagedDevice( - caller.getUserId(), + deviceOwnerUserId, provisioningParams.isLeaveAllSystemAppsEnabled(), deviceAdmin)) { throw new ServiceSpecificException( @@ -16327,15 +16339,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "PackageManager failed to remove non required apps."); } + if (!setActiveAdminAndDeviceOwner( - caller.getUserId(), deviceAdmin, provisioningParams.getOwnerName())) { + deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) { throw new ServiceSpecificException( PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED, "Failed to set device owner."); } disallowAddUser(); - setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(), + setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId, provisioningParams.canDeviceOwnerGrantSensorsPermissions()); } catch (Exception e) { DevicePolicyEventLogger @@ -16378,30 +16391,42 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean removeNonRequiredAppsForManagedDevice( - int userId, boolean leaveAllSystemAppsEnabled, ComponentName admin) { + @UserIdInt int userId, boolean leaveAllSystemAppsEnabled, ComponentName admin) { Set<String> packagesToDelete = leaveAllSystemAppsEnabled ? Collections.emptySet() : mOverlayPackagesProvider.getNonRequiredApps( admin, userId, ACTION_PROVISION_MANAGED_DEVICE); + + removeNonInstalledPackages(packagesToDelete, userId); if (packagesToDelete.isEmpty()) { + Slog.i(LOG_TAG, "No packages to delete on user " + userId); return true; } + NonRequiredPackageDeleteObserver packageDeleteObserver = new NonRequiredPackageDeleteObserver(packagesToDelete.size()); for (String packageName : packagesToDelete) { - if (isPackageInstalledForUser(packageName, userId)) { - Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId); - mContext.getPackageManager().deletePackageAsUser( - packageName, - packageDeleteObserver, - PackageManager.DELETE_SYSTEM_APP, - userId); - } + Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId); + mContext.getPackageManager().deletePackageAsUser( + packageName, + packageDeleteObserver, + PackageManager.DELETE_SYSTEM_APP, + userId); } Slog.i(LOG_TAG, "Waiting for non required apps to be deleted"); return packageDeleteObserver.awaitPackagesDeletion(); } + private void removeNonInstalledPackages(Set<String> packages, @UserIdInt int userId) { + final Set<String> toBeRemoved = new HashSet<>(); + for (String packageName : packages) { + if (!isPackageInstalledForUser(packageName, userId)) { + toBeRemoved.add(packageName); + } + } + packages.removeAll(toBeRemoved); + } + private void disallowAddUser() { if (mInjector.userManagerIsHeadlessSystemUserMode()) { Slog.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode."); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 98c3b99124ee..97e75828ed91 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -525,8 +525,7 @@ public final class SystemServer implements Dumpable { String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof"; Debug.dumpHprofData(filename); } catch (IOException ex) { - Slog.e("System", "Failed to dump fdtrack hprof"); - ex.printStackTrace(); + Slog.e("System", "Failed to dump fdtrack hprof", ex); } } diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java index 86054e48fe6d..27fce3c37fd9 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java @@ -18,13 +18,17 @@ package com.android.server.graphics.fonts; import static com.google.common.truth.Truth.assertThat; +import android.graphics.FontListParser; import android.platform.test.annotations.Presubmit; +import android.text.FontConfig; +import android.util.Xml; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; @@ -38,13 +42,19 @@ import java.nio.charset.StandardCharsets; public final class PersistentSystemFontConfigTest { @Test - public void testWriteRead() throws IOException, XmlPullParserException { + public void testWriteRead() throws Exception { long expectedModifiedDate = 1234567890; PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); config.lastModifiedDate = expectedModifiedDate; config.updatedFontDirs.add("~~abc"); config.updatedFontDirs.add("~~def"); + FontConfig.FontFamily fontFamily = parseFontFamily( + "<family name='test'>" + + " <font>test.ttf</font>" + + "</family>"); + config.fontFamilies.add(fontFamily); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PersistentSystemFontConfig.writeToXml(baos, config); @@ -57,6 +67,7 @@ public final class PersistentSystemFontConfigTest { assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate); assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def"); + assertThat(another.fontFamilies).containsExactly(fontFamily); } } } @@ -75,4 +86,11 @@ public final class PersistentSystemFontConfigTest { } } + private static FontConfig.FontFamily parseFontFamily(String xml) throws Exception { + XmlPullParser parser = Xml.newPullParser(); + ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); + parser.setInput(is, "UTF-8"); + parser.nextTag(); + return FontListParser.readFamily(parser, "", null); + } } diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index cb83b0f7f2f7..4bbf96fcb7ef 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -22,12 +22,15 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.fail; import android.content.Context; +import android.graphics.FontListParser; import android.graphics.fonts.FontManager; import android.graphics.fonts.FontUpdateRequest; import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.Presubmit; import android.system.Os; +import android.text.FontConfig; +import android.util.Xml; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -37,7 +40,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -49,6 +54,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @Presubmit @SmallTest @@ -157,7 +163,11 @@ public final class UpdatableFontDirTest { newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar,4", GOOD_SIGNATURE))); + newFontUpdateRequest("bar,4", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family name='foobar'>" + + " <font>foo.ttf</font>" + + " <font>bar.ttf</font>" + + "</family>"))); // Four font dirs are created. assertThat(mUpdatableFontFilesDir.list()).hasLength(4); assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) @@ -173,6 +183,14 @@ public final class UpdatableFontDirTest { assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4); // Outdated font dir should be deleted. assertThat(mUpdatableFontFilesDir.list()).hasLength(2); + assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar"); + assertThat(dir.getFontFamilyMap()).containsKey("foobar"); + FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar"); + assertThat(foobar.getFontList()).hasSize(2); + assertThat(foobar.getFontList().get(0).getFile()) + .isEqualTo(dir.getFontFileMap().get("foo.ttf")); + assertThat(foobar.getFontList().get(1).getFile()) + .isEqualTo(dir.getFontFileMap().get("bar.ttf")); } @Test @@ -184,6 +202,7 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getFontFamilyMap()).isEmpty(); } @Test @@ -198,7 +217,11 @@ public final class UpdatableFontDirTest { newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar,4", GOOD_SIGNATURE))); + newFontUpdateRequest("bar,4", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family name='foobar'>" + + " <font>foo.ttf</font>" + + " <font>bar.ttf</font>" + + "</family>"))); // Four font dirs are created. assertThat(mUpdatableFontFilesDir.list()).hasLength(4); @@ -211,6 +234,7 @@ public final class UpdatableFontDirTest { assertThat(dir.getFontFileMap()).isEmpty(); // All font dirs (including dir for "bar.ttf") should be deleted. assertThat(mUpdatableFontFilesDir.list()).hasLength(0); + assertThat(dir.getFontFamilyMap()).isEmpty(); } @Test @@ -225,7 +249,11 @@ public final class UpdatableFontDirTest { newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar,4", GOOD_SIGNATURE))); + newFontUpdateRequest("bar,4", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family name='foobar'>" + + " <font>foo.ttf</font>" + + " <font>bar.ttf</font>" + + "</family>"))); // Four font dirs are created. assertThat(mUpdatableFontFilesDir.list()).hasLength(4); @@ -239,6 +267,7 @@ public final class UpdatableFontDirTest { assertThat(dir.getFontFileMap()).isEmpty(); // All font dirs (including dir for "bar.ttf") should be deleted. assertThat(mUpdatableFontFilesDir.list()).hasLength(0); + assertThat(dir.getFontFamilyMap()).isEmpty(); } @Test @@ -253,7 +282,11 @@ public final class UpdatableFontDirTest { newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar,4", GOOD_SIGNATURE))); + newFontUpdateRequest("bar,4", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family name='foobar'>" + + " <font>foo.ttf</font>" + + " <font>bar.ttf</font>" + + "</family>"))); // Four font dirs are created. assertThat(mUpdatableFontFilesDir.list()).hasLength(4); @@ -274,6 +307,8 @@ public final class UpdatableFontDirTest { // We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled // fonts. assertThat(mUpdatableFontFilesDir.list()).hasLength(1); + // Font family depending on obsoleted font should be removed. + assertThat(dir.getFontFamilyMap()).isEmpty(); } @Test @@ -285,6 +320,7 @@ public final class UpdatableFontDirTest { new File("/dev/null")); dir.loadFontFileMap(); assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getFontFamilyMap()).isEmpty(); } @Test @@ -295,12 +331,19 @@ public final class UpdatableFontDirTest { mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, mConfigFile); dirForPreparation.loadFontFileMap(); - dirForPreparation.update( - Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); + dirForPreparation.update(Arrays.asList( + newFontUpdateRequest("foo,1", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family name='foobar'>" + + " <font>foo.ttf</font>" + + "</family>"))); try { dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,2", GOOD_SIGNATURE), - newFontUpdateRequest("bar,2", "Invalid signature"))); + newFontUpdateRequest("bar,2", "Invalid signature"), + newAddFontFamilyRequest("<family name='foobar'>" + + " <font>foo.ttf</font>" + + " <font>bar.ttf</font>" + + "</family>"))); fail("Batch update with invalid signature should fail"); } catch (FontManagerService.SystemFontException e) { // Expected @@ -313,6 +356,11 @@ public final class UpdatableFontDirTest { // The state should be rolled back as a whole if one of the update requests fail. assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); + assertThat(dir.getFontFamilyMap()).containsKey("foobar"); + FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar"); + assertThat(foobar.getFontList()).hasSize(1); + assertThat(foobar.getFontList().get(0).getFile()) + .isEqualTo(dir.getFontFileMap().get("foo.ttf")); } @Test @@ -364,7 +412,7 @@ public final class UpdatableFontDirTest { dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE))); try { dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); - fail("Expect IllegalArgumentException"); + fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); } @@ -440,7 +488,7 @@ public final class UpdatableFontDirTest { try { dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); - fail("Expect IllegalArgumentException"); + fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); } @@ -599,6 +647,127 @@ public final class UpdatableFontDirTest { assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); } + @Test + public void addFontFamily() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); + UpdatableFontDir dir = new UpdatableFontDir( + mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, + mConfigFile); + dir.loadFontFileMap(); + + dir.update(Arrays.asList( + newFontUpdateRequest("test,1", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family name='test'>" + + " <font>test.ttf</font>" + + "</family>"))); + assertThat(dir.getFontFileMap()).containsKey("test.ttf"); + assertThat(dir.getFontFamilyMap()).containsKey("test"); + FontConfig.FontFamily test = dir.getFontFamilyMap().get("test"); + assertThat(test.getFontList()).hasSize(1); + assertThat(test.getFontList().get(0).getFile()) + .isEqualTo(dir.getFontFileMap().get("test.ttf")); + } + + @Test + public void addFontFamily_noName() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); + UpdatableFontDir dir = new UpdatableFontDir( + mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, + mConfigFile); + dir.loadFontFileMap(); + + try { + dir.update(Arrays.asList( + newFontUpdateRequest("test,1", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family lang='en'>" + + " <font>test.ttf</font>" + + "</family>"))); + fail("Expect IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expect + } + } + + @Test + public void addFontFamily_fontNotAvailable() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); + UpdatableFontDir dir = new UpdatableFontDir( + mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, + mConfigFile); + dir.loadFontFileMap(); + + try { + dir.update(Arrays.asList(newAddFontFamilyRequest("<family name='test'>" + + " <font>test.ttf</font>" + + "</family>"))); + fail("Expect IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expect + } + } + + @Test + public void getSystemFontConfig() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); + UpdatableFontDir dir = new UpdatableFontDir( + mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, + mConfigFile); + dir.loadFontFileMap(); + // We assume we have monospace. + assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace"); + + dir.update(Arrays.asList( + newFontUpdateRequest("test,1", GOOD_SIGNATURE), + // Updating an existing font family. + newAddFontFamilyRequest("<family name='monospace'>" + + " <font>test.ttf</font>" + + "</family>"), + // Adding a new font family. + newAddFontFamilyRequest("<family name='test'>" + + " <font>test.ttf</font>" + + "</family>"))); + FontConfig fontConfig = dir.getSystemFontConfig(); + assertNamedFamilyExists(fontConfig, "monospace"); + FontConfig.FontFamily monospace = getLastFamily(fontConfig, "monospace"); + assertThat(monospace.getFontList()).hasSize(1); + assertThat(monospace.getFontList().get(0).getFile()) + .isEqualTo(dir.getFontFileMap().get("test.ttf")); + assertNamedFamilyExists(fontConfig, "test"); + assertThat(getLastFamily(fontConfig, "test").getFontList()) + .isEqualTo(monospace.getFontList()); + } + + @Test + public void getSystemFontConfig_preserveFirstFontFamily() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); + UpdatableFontDir dir = new UpdatableFontDir( + mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, + mConfigFile); + dir.loadFontFileMap(); + assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty(); + FontConfig.FontFamily firstFontFamily = dir.getSystemFontConfig().getFontFamilies().get(0); + assertThat(firstFontFamily.getName()).isNotEmpty(); + + dir.update(Arrays.asList( + newFontUpdateRequest("test,1", GOOD_SIGNATURE), + newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>" + + " <font>test.ttf</font>" + + "</family>"))); + FontConfig fontConfig = dir.getSystemFontConfig(); + assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty(); + assertThat(fontConfig.getFontFamilies().get(0)).isEqualTo(firstFontFamily); + FontConfig.FontFamily updated = getLastFamily(fontConfig, firstFontFamily.getName()); + assertThat(updated.getFontList()).hasSize(1); + assertThat(updated.getFontList().get(0).getFile()) + .isEqualTo(dir.getFontFileMap().get("test.ttf")); + assertThat(updated).isNotEqualTo(firstFontFamily); + } + private FontUpdateRequest newFontUpdateRequest(String content, String signature) throws Exception { File file = File.createTempFile("font", "ttf", mCacheDir); @@ -608,10 +777,36 @@ public final class UpdatableFontDirTest { signature.getBytes()); } + private static FontUpdateRequest newAddFontFamilyRequest(String xml) throws Exception { + XmlPullParser parser = Xml.newPullParser(); + ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); + parser.setInput(is, "UTF-8"); + parser.nextTag(); + FontConfig.FontFamily fontFamily = FontListParser.readFamily(parser, "", null); + return new FontUpdateRequest(fontFamily); + } + private void writeConfig(PersistentSystemFontConfig.Config config, File file) throws IOException { try (FileOutputStream fos = new FileOutputStream(file)) { PersistentSystemFontConfig.writeToXml(fos, config); } } + + // Returns the last family with the given name, which will be used for creating Typeface. + private static FontConfig.FontFamily getLastFamily(FontConfig fontConfig, String familyName) { + List<FontConfig.FontFamily> fontFamilies = fontConfig.getFontFamilies(); + for (int i = fontFamilies.size() - 1; i >= 0; i--) { + if (familyName.equals(fontFamilies.get(i).getName())) { + return fontFamilies.get(i); + } + } + return null; + } + + private static void assertNamedFamilyExists(FontConfig fontConfig, String familyName) { + assertThat(fontConfig.getFontFamilies().stream() + .map(FontConfig.FontFamily::getName) + .collect(Collectors.toSet())).contains(familyName); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java index da9dcb79fb76..7cb72c414e52 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java @@ -16,6 +16,7 @@ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -28,6 +29,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.media.AudioManager; @@ -214,4 +216,75 @@ public class DevicePowerStatusActionTest { verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_UNKNOWN); } + + @Test + public void queryDisplayStatus_localDevice_2_0_targetDevice_1_4() throws Exception { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mPlaybackDevice.mAddress, ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(response); + mTestLooper.dispatchAll(); + + verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); + } + + @Test + public void queryDisplayStatus_localDevice_2_0_targetDevice_2_0() throws Exception { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder + .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + HdmiCecMessage reportPowerStatusBroadcast = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, ADDR_BROADCAST, HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(reportPowerStatusBroadcast); + mTestLooper.dispatchAll(); + mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mPlaybackDevice.mAddress, ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus); + + verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); + } + + @Test + public void queryDisplayStatus_localDevice_2_0_targetDevice_2_0_unknown() throws Exception { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder + .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + HdmiCecMessage reportPowerStatusBroadcast = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, ADDR_BROADCAST, HdmiControlManager.POWER_STATUS_UNKNOWN); + mNativeWrapper.onCecMessage(reportPowerStatusBroadcast); + mTestLooper.dispatchAll(); + mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mPlaybackDevice.mAddress, ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(response); + mTestLooper.dispatchAll(); + + verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index e6b56ca3418f..9f0d9829df01 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -67,6 +67,7 @@ public class HdmiCecLocalDevicePlaybackTest { private int mPlaybackLogicalAddress; private boolean mWokenUp; private boolean mStandby; + private boolean mActiveMediaSessionsPaused; @Mock private IPowerManager mIPowerManagerMock; @@ -97,6 +98,11 @@ public class HdmiCecLocalDevicePlaybackTest { } @Override + void pauseActiveMediaSessions() { + mActiveMediaSessionsPaused = true; + } + + @Override boolean isStandbyMessageReceived() { return mStandby; } @@ -392,6 +398,54 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleRoutingChange_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleRoutingChange_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingChange_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingChange_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void handleRoutingInformation_otherDevice_None() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, @@ -496,6 +550,52 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleRoutingInformation_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleRoutingInformation_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingInformation_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingInformation_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void handleSetStreamPath() { HdmiCecMessage message = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100); @@ -831,6 +931,52 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleActiveSource_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleActiveSource_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleActiveSource_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleActiveSource_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void losingActiveSource_standbyNow_verifyStandbyMessageIsSentOnNextStandby() { // As described in b/161097846. mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( @@ -1158,6 +1304,54 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleSetStreamPath_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleSetStreamPath_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleSetStreamPath_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleSetStreamPath_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void oneTouchPlay_SendStandbyOnSleepToTv() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java index 25dbc6b6cd51..33ea7108a705 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java @@ -45,6 +45,7 @@ import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.Looper; +import android.os.Process; import android.os.ResultReceiver; import android.os.ShellCallback; import android.platform.test.annotations.Presubmit; @@ -89,7 +90,8 @@ public class LockSettingsShellCommandTest { MockitoAnnotations.initMocks(this); final Context context = InstrumentationRegistry.getTargetContext(); mUserId = ActivityManager.getCurrentUser(); - mCommand = new LockSettingsShellCommand(mLockPatternUtils); + mCommand = new LockSettingsShellCommand(mLockPatternUtils, context, 0, + Process.SHELL_UID); when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true); } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt index 3a292deeaac9..38125c7847f3 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt +++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt @@ -21,7 +21,9 @@ import android.content.om.OverlayableInfo import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.os.Process +import android.util.ArrayMap import com.android.server.om.OverlayActorEnforcer.ActorState +import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat @@ -29,6 +31,7 @@ import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized +import org.mockito.Mockito import org.mockito.Mockito.spy import java.io.IOException @@ -125,11 +128,11 @@ class OverlayActorEnforcerTests { ActorState.TARGET_NOT_FOUND withCases { failure("nullPkgInfo") { targetPkgInfo = null } allowed("debuggable") { - targetPkgInfo = pkgInfo(TARGET_PKG).apply { - applicationInfo.flags = ApplicationInfo.FLAG_DEBUGGABLE + targetPkgInfo = androidPackage(TARGET_PKG).apply { + whenever(this.isDebuggable).thenReturn(true) } } - skip { targetPkgInfo = pkgInfo(TARGET_PKG) } + skip { targetPkgInfo = androidPackage(TARGET_PKG) } }, ActorState.NO_PACKAGES_FOR_UID withCases { failure("empty") { callingUid = EMPTY_UID } @@ -236,22 +239,20 @@ class OverlayActorEnforcerTests { mapOf(VALID_ACTOR_NAME to VALID_ACTOR_PKG)) } }, - ActorState.MISSING_APP_INFO withCases { + ActorState.ACTOR_NOT_FOUND withCases { failure("nullActorPkgInfo") { actorPkgInfo = null } failure("nullActorAppInfo") { - actorPkgInfo = PackageInfo().apply { applicationInfo = null } + actorPkgInfo = null } - skip { actorPkgInfo = pkgInfo(VALID_ACTOR_PKG) } + skip { actorPkgInfo = androidPackage(VALID_ACTOR_PKG) } }, ActorState.ACTOR_NOT_PREINSTALLED withCases { failure("notSystem") { - actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply { - applicationInfo.flags = 0 - } + actorPkgInfo = androidPackage(VALID_ACTOR_PKG) } skip { - actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply { - applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM + actorPkgInfo = androidPackage(VALID_ACTOR_PKG).apply { + whenever(this.isSystem).thenReturn(true) } } }, @@ -272,22 +273,22 @@ class OverlayActorEnforcerTests { ) { fun toOverlayInfo() = OverlayInfo( OVERLAY_PKG, + "", targetPackageName, targetOverlayableName, null, "/path", OverlayInfo.STATE_UNKNOWN, 0, - 0, false) + 0, false, false) } private infix fun ActorState.withCases(block: TestCase.() -> Unit) = TestCase(this).apply(block) - private fun pkgInfo(pkgName: String): PackageInfo = mockThrowOnUnmocked { - this.packageName = pkgName - this.applicationInfo = ApplicationInfo().apply { - this.packageName = pkgName - } + private fun androidPackage(pkgName: String): AndroidPackage = mockThrowOnUnmocked { + whenever(this.packageName).thenReturn(pkgName) + whenever(this.isDebuggable).thenReturn(false) + whenever(this.isSystem).thenReturn(false) } private fun makeTestName(testCase: TestCase, caseName: String, type: Params.Type): String { @@ -363,8 +364,8 @@ class OverlayActorEnforcerTests { var namedActorsMap: Map<String, Map<String, String>> = emptyMap(), var hasPermission: Boolean = false, var targetOverlayableInfo: OverlayableInfo? = null, - var targetPkgInfo: PackageInfo? = null, - var actorPkgInfo: PackageInfo? = null, + var targetPkgInfo: AndroidPackage? = null, + var actorPkgInfo: AndroidPackage? = null, vararg val packageNames: String = arrayOf("com.test.actor.one") ) : PackageManagerHelper { @@ -375,6 +376,14 @@ class OverlayActorEnforcerTests { override fun getNamedActors() = namedActorsMap + override fun isInstantApp(packageName: String, userId: Int): Boolean { + throw UnsupportedOperationException() + } + + override fun initializeForUser(userId: Int): ArrayMap<String, AndroidPackage> { + throw UnsupportedOperationException() + } + @Throws(IOException::class) override fun getOverlayableForTarget( packageName: String, @@ -394,9 +403,6 @@ class OverlayActorEnforcerTests { else -> null } - override fun getPackageInfo(packageName: String, userId: Int) = - listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName } - @Throws(IOException::class) // Mockito requires this checked exception to be declared override fun doesTargetDefineOverlayable(targetPackageName: String?, userId: Int): Boolean { return targetOverlayableInfo?.takeIf { @@ -411,11 +417,10 @@ class OverlayActorEnforcerTests { } } - override fun getConfigSignaturePackage(): String { - throw UnsupportedOperationException() - } + override fun getPackageForUser(packageName: String, userId: Int) = + listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName } - override fun getOverlayPackages(userId: Int): MutableList<PackageInfo> { + override fun getConfigSignaturePackage(): String { throw UnsupportedOperationException() } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java index 5468fba59c10..55cd772ad7e0 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java @@ -21,7 +21,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; +import android.util.ArraySet; import androidx.test.runner.AndroidJUnit4; @@ -29,77 +31,66 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; -import java.util.List; import java.util.function.BiConsumer; @RunWith(AndroidJUnit4.class) public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase { private static final String OVERLAY = "com.test.overlay"; + private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY); private static final String TARGET = "com.test.target"; private static final int USER = 0; private static final String OVERLAY2 = OVERLAY + "2"; + private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2); @Test public void testUpdateOverlaysForUser() { final OverlayManagerServiceImpl impl = getImpl(); + final String otherTarget = "some.other.target"; addPackage(target(TARGET), USER); - addPackage(target("some.other.target"), USER); + addPackage(target(otherTarget), USER); addPackage(overlay(OVERLAY, TARGET), USER); // do nothing, expect no change - final List<String> a = impl.updateOverlaysForUser(USER); - assertEquals(1, a.size()); - assertTrue(a.contains(TARGET)); - - // upgrade overlay, keep target - addPackage(overlay(OVERLAY, TARGET), USER); - - final List<String> b = impl.updateOverlaysForUser(USER); - assertEquals(1, b.size()); - assertTrue(b.contains(TARGET)); - - // do nothing, expect no change - final List<String> c = impl.updateOverlaysForUser(USER); - assertEquals(1, c.size()); - assertTrue(c.contains(TARGET)); - - // upgrade overlay, switch to new target - addPackage(overlay(OVERLAY, "some.other.target"), USER); - final List<String> d = impl.updateOverlaysForUser(USER); - assertEquals(2, d.size()); - assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target"))); - - // do nothing, expect no change - final List<String> f = impl.updateOverlaysForUser(USER); - assertEquals(1, f.size()); - assertTrue(f.contains("some.other.target")); + final ArraySet<PackageAndUser> a = impl.updateOverlaysForUser(USER); + assertEquals(3, a.size()); + assertTrue(a.containsAll(Arrays.asList( + new PackageAndUser(TARGET, USER), + new PackageAndUser(otherTarget, USER), + new PackageAndUser(OVERLAY, USER)))); + + final ArraySet<PackageAndUser> b = impl.updateOverlaysForUser(USER); + assertEquals(3, b.size()); + assertTrue(b.containsAll(Arrays.asList( + new PackageAndUser(TARGET, USER), + new PackageAndUser(otherTarget, USER), + new PackageAndUser(OVERLAY, USER)))); } @Test public void testImmutableEnabledChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertFalse(o1.isEnabled()); assertFalse(o1.isMutable); configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o2); assertTrue(o2.isEnabled()); assertFalse(o2.isMutable); configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertFalse(o3.isEnabled()); assertFalse(o3.isMutable); @@ -108,26 +99,26 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutableEnabledChangeHasNoEffect() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertFalse(o1.isEnabled()); assertTrue(o1.isMutable); configureSystemOverlay(OVERLAY, true /* mutable */, true /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o2); assertFalse(o2.isEnabled()); assertTrue(o2.isMutable); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertFalse(o3.isEnabled()); assertTrue(o3.isMutable); @@ -136,13 +127,13 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutableEnabledToImmutableEnabled() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); final BiConsumer<Boolean, Boolean> setOverlay = (mutable, enabled) -> { configureSystemOverlay(OVERLAY, mutable, enabled, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o); assertEquals(enabled, o.isEnabled()); assertEquals(mutable, o.isMutable); @@ -180,38 +171,38 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutablePriorityChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 1 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertEquals(0, o1.priority); assertFalse(o1.isEnabled()); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o2); assertEquals(1, o2.priority); assertFalse(o2.isEnabled()); // Overlay priority changing between reboots should not affect enable state of mutable // overlays. - impl.setEnabled(OVERLAY, true, USER); + impl.setEnabled(IDENTIFIER, true, USER); // Reorder the overlays configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 1 /* priority */); configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertEquals(1, o3.priority); assertTrue(o3.isEnabled()); - final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o4); assertEquals(0, o4.priority); assertFalse(o4.isEnabled()); @@ -220,19 +211,19 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testImmutablePriorityChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */); configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 1 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertEquals(0, o1.priority); assertTrue(o1.isEnabled()); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o2); assertEquals(1, o2.priority); assertTrue(o2.isEnabled()); @@ -242,12 +233,12 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertEquals(1, o3.priority); assertTrue(o3.isEnabled()); - final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o4); assertEquals(0, o4.priority); assertTrue(o4.isEnabled()); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java index 33dbcc0855be..45f82a36c8ed 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.testng.Assert.assertThrows; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.util.Pair; @@ -39,19 +40,23 @@ import org.junit.runner.RunWith; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; @RunWith(AndroidJUnit4.class) public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase { private static final String OVERLAY = "com.test.overlay"; + private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY); private static final String TARGET = "com.test.target"; private static final int USER = 0; private static final String OVERLAY2 = OVERLAY + "2"; private static final String TARGET2 = TARGET + "2"; + private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2); private static final int USER2 = USER + 1; private static final String OVERLAY3 = OVERLAY + "3"; + private static final OverlayIdentifier IDENTIFIER3 = new OverlayIdentifier(OVERLAY3); private static final int USER3 = USER2 + 1; private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref"; @@ -60,10 +65,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testGetOverlayInfo() throws Exception { - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); final OverlayManagerServiceImpl impl = getImpl(); - final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo oi = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(oi); assertEquals(oi.packageName, OVERLAY); assertEquals(oi.targetPackageName, TARGET); @@ -72,19 +77,19 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testGetOverlayInfosForTarget() throws Exception { - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); - installNewPackage(overlay(OVERLAY3, TARGET), USER2); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(overlay(OVERLAY3, TARGET), USER2); final OverlayManagerServiceImpl impl = getImpl(); final List<OverlayInfo> ois = impl.getOverlayInfosForTarget(TARGET, USER); assertEquals(ois.size(), 2); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER))); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER))); final List<OverlayInfo> ois2 = impl.getOverlayInfosForTarget(TARGET, USER2); assertEquals(ois2.size(), 1); - assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER2))); + assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER2))); final List<OverlayInfo> ois3 = impl.getOverlayInfosForTarget(TARGET, USER3); assertNotNull(ois3); @@ -97,10 +102,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testGetOverlayInfosForUser() throws Exception { - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); - installNewPackage(overlay(OVERLAY3, TARGET2), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(overlay(OVERLAY3, TARGET2), USER); final OverlayManagerServiceImpl impl = getImpl(); final Map<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER); @@ -109,13 +114,13 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes final List<OverlayInfo> ois = everything.get(TARGET); assertNotNull(ois); assertEquals(ois.size(), 2); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER))); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER))); final List<OverlayInfo> ois2 = everything.get(TARGET2); assertNotNull(ois2); assertEquals(ois2.size(), 1); - assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER))); + assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER))); final Map<String, List<OverlayInfo>> everything2 = impl.getOverlaysForUser(USER2); assertNotNull(everything2); @@ -124,26 +129,26 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testPriority() throws Exception { - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); - installNewPackage(overlay(OVERLAY3, TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(overlay(OVERLAY3, TARGET), USER); final OverlayManagerServiceImpl impl = getImpl(); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY3, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER3, USER); assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); - assertEquals(impl.setLowestPriority(OVERLAY3, USER), + assertEquals(impl.setLowestPriority(IDENTIFIER3, USER), Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2); - assertEquals(impl.setHighestPriority(OVERLAY3, USER), - Optional.of(new PackageAndUser(TARGET, USER))); + assertEquals(impl.setHighestPriority(IDENTIFIER3, USER), + Set.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); - assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER), + assertEquals(impl.setPriority(IDENTIFIER, IDENTIFIER2, USER), Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3); } @@ -151,61 +156,63 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testOverlayInfoStateTransitions() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - assertNull(impl.getOverlayInfo(OVERLAY, USER)); + assertNull(impl.getOverlayInfo(IDENTIFIER, USER)); - installNewPackage(overlay(OVERLAY, TARGET), USER); - assertState(STATE_MISSING_TARGET, OVERLAY, USER); + installPackage(overlay(OVERLAY, TARGET), USER); + assertState(STATE_MISSING_TARGET, IDENTIFIER, USER); final FakeDeviceState.PackageBuilder target = target(TARGET); - installNewPackage(target, USER); - assertState(STATE_DISABLED, OVERLAY, USER); + installPackage(target, USER); + assertState(STATE_DISABLED, IDENTIFIER, USER); - assertEquals(impl.setEnabled(OVERLAY, true, USER), - Optional.of(new PackageAndUser(TARGET, USER))); - assertState(STATE_ENABLED, OVERLAY, USER); + assertEquals(impl.setEnabled(IDENTIFIER, true, USER), + Set.of(new PackageAndUser(TARGET, USER))); + assertState(STATE_ENABLED, IDENTIFIER, USER); // target upgrades do not change the state of the overlay upgradePackage(target, USER); - assertState(STATE_ENABLED, OVERLAY, USER); + assertState(STATE_ENABLED, IDENTIFIER, USER); uninstallPackage(TARGET, USER); - assertState(STATE_MISSING_TARGET, OVERLAY, USER); + assertState(STATE_MISSING_TARGET, IDENTIFIER, USER); - installNewPackage(target, USER); - assertState(STATE_ENABLED, OVERLAY, USER); + installPackage(target, USER); + assertState(STATE_ENABLED, IDENTIFIER, USER); } @Test public void testOnOverlayPackageUpgraded() throws Exception { final FakeDeviceState.PackageBuilder target = target(TARGET); final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET); - installNewPackage(target, USER); - installNewPackage(overlay, USER); + installPackage(target, USER); + installPackage(overlay, USER); upgradePackage(overlay, USER); // upgrade to a version where the overlay has changed its target final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target"); - final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair = - upgradePackage(overlay2, USER); - assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER))); - assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER))); + final Pair<Set<PackageAndUser>, Set<PackageAndUser>> pair = upgradePackage(overlay2, USER); + assertEquals(pair.first, Set.of(new PackageAndUser(TARGET, USER))); + assertEquals( + Set.of(new PackageAndUser(TARGET, USER), + new PackageAndUser("some.other.target", USER)), + pair.second); } @Test public void testSetEnabledAtVariousConditions() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); assertThrows(OverlayManagerServiceImpl.OperationFailedException.class, - () -> impl.setEnabled(OVERLAY, true, USER)); + () -> impl.setEnabled(IDENTIFIER, true, USER)); // request succeeded, and there was a change that needs to be // propagated to the rest of the system - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - assertEquals(impl.setEnabled(OVERLAY, true, USER), - Optional.of(new PackageAndUser(TARGET, USER))); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + assertEquals(impl.setEnabled(IDENTIFIER, true, USER), + Set.of(new PackageAndUser(TARGET, USER))); // request succeeded, but nothing changed - assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent()); + assertTrue(impl.setEnabled(IDENTIFIER, true, USER).isEmpty()); } @Test @@ -214,8 +221,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes reinitializeImpl(); addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -232,8 +239,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes reinitializeImpl(); addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -247,8 +254,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testConfigSignaturePolicyNoConfig() throws Exception { addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -261,8 +268,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testConfigSignaturePolicyNoRefPkg() throws Exception { - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -279,8 +286,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes reinitializeImpl(); addPackage(app(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java index 2c477c897b30..16e03290b1e4 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java @@ -24,11 +24,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.annotation.NonNull; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayInfo.State; import android.content.om.OverlayableInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; +import android.os.FabricatedOverlayInfo; +import android.os.FabricatedOverlayInternal; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -37,17 +38,19 @@ import android.util.Pair; import androidx.annotation.Nullable; import com.android.internal.content.om.OverlayConfig; +import com.android.internal.util.CollectionUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import org.junit.Assert; import org.junit.Before; +import org.mockito.Mockito; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; +import java.util.Set; /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */ class OverlayManagerServiceImplTestsBase { @@ -94,12 +97,11 @@ class OverlayManagerServiceImplTestsBase { mConfigSignaturePackageName = packageName; } - void assertState(@State int expected, final String overlayPackageName, int userId) { - final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId); + void assertState(@State int expected, final OverlayIdentifier overlay, int userId) { + final OverlayInfo info = mImpl.getOverlayInfo(overlay, userId); if (info == null) { - throw new IllegalStateException("package not installed"); + throw new IllegalStateException("overlay '" + overlay + "' not installed"); } - final String msg = String.format("expected %s but was %s:", OverlayInfo.stateToString(expected), OverlayInfo.stateToString(info.state)); assertEquals(msg, expected, info.state); @@ -152,17 +154,13 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is currently installed */ - void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) + Set<PackageAndUser> installPackage(FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException { if (mState.select(pkg.packageName, userId) != null) { throw new IllegalStateException("package " + pkg.packageName + " already installed"); } mState.add(pkg, userId); - if (pkg.targetPackage == null) { - mImpl.onTargetPackageAdded(pkg.packageName, userId); - } else { - mImpl.onOverlayPackageAdded(pkg.packageName, userId); - } + return CollectionUtils.emptyIfNull(mImpl.onPackageAdded(pkg.packageName, userId)); } /** @@ -178,26 +176,21 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is not currently installed */ - Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage( + Pair<Set<PackageAndUser>, Set<PackageAndUser>> upgradePackage( FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException { final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); if (replacedPackage == null) { throw new IllegalStateException("package " + pkg.packageName + " not installed"); } - Optional<PackageAndUser> opt1 = Optional.empty(); - if (replacedPackage.targetPackageName != null) { - opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId); - } + + final Set<PackageAndUser> updatedPackages1 = + CollectionUtils.emptyIfNull(mImpl.onPackageReplacing(pkg.packageName, userId)); mState.add(pkg, userId); - Optional<PackageAndUser> opt2; - if (pkg.targetPackage == null) { - opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId); - } else { - opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId); - } + final Set<PackageAndUser> updatedPackages2 = + CollectionUtils.emptyIfNull(mImpl.onPackageReplaced(pkg.packageName, userId)); - return Pair.create(opt1, opt2); + return Pair.create(updatedPackages1, updatedPackages2); } /** @@ -208,17 +201,13 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is not currently installed */ - void uninstallPackage(String packageName, int userId) throws OperationFailedException { + Set<PackageAndUser> uninstallPackage(String packageName, int userId) { final FakeDeviceState.Package pkg = mState.select(packageName, userId); if (pkg == null) { throw new IllegalStateException("package " + packageName+ " not installed"); } mState.remove(pkg.packageName); - if (pkg.targetPackageName == null) { - mImpl.onTargetPackageRemoved(pkg.packageName, userId); - } else { - mImpl.onOverlayPackageRemoved(pkg.packageName, userId); - } + return CollectionUtils.emptyIfNull(mImpl.onPackageRemoved(packageName, userId)); } /** Represents the state of packages installed on a fake device. */ @@ -247,11 +236,6 @@ class OverlayManagerServiceImplTestsBase { } } - List<Package> select(int userId) { - return mPackages.values().stream().filter(p -> p.installedUserIds.contains(userId)) - .collect(Collectors.toList()); - } - Package select(String packageName, int userId) { final Package pkg = mPackages.get(packageName); return pkg != null && pkg.installedUserIds.contains(userId) ? pkg : null; @@ -335,6 +319,21 @@ class OverlayManagerServiceImplTestsBase { this.apkPath = apkPath; this.certificate = certificate; } + + @Nullable + private AndroidPackage getPackageForUser(int user) { + if (!installedUserIds.contains(user)) { + return null; + } + final AndroidPackage pkg = Mockito.mock(AndroidPackage.class); + when(pkg.getPackageName()).thenReturn(packageName); + when(pkg.getBaseApkPath()).thenReturn(apkPath); + when(pkg.getLongVersionCode()).thenReturn((long) versionCode); + when(pkg.getOverlayTarget()).thenReturn(targetPackageName); + when(pkg.getOverlayTargetName()).thenReturn(targetOverlayableName); + when(pkg.getOverlayCategory()).thenReturn("Fake-category-" + targetPackageName); + return pkg; + } } } @@ -345,21 +344,29 @@ class OverlayManagerServiceImplTestsBase { mState = state; } + @NonNull @Override - public PackageInfo getPackageInfo(@NonNull String packageName, int userId) { - final FakeDeviceState.Package pkg = mState.select(packageName, userId); - if (pkg == null) { - return null; - } - final ApplicationInfo ai = new ApplicationInfo(); - ai.sourceDir = pkg.apkPath; - PackageInfo pi = new PackageInfo(); - pi.applicationInfo = ai; - pi.packageName = pkg.packageName; - pi.overlayTarget = pkg.targetPackageName; - pi.targetOverlayableName = pkg.targetOverlayableName; - pi.overlayCategory = "Fake-category-" + pkg.targetPackageName; - return pi; + public ArrayMap<String, AndroidPackage> initializeForUser(int userId) { + final ArrayMap<String, AndroidPackage> packages = new ArrayMap<>(); + mState.mPackages.forEach((key, value) -> { + final AndroidPackage pkg = value.getPackageForUser(userId); + if (pkg != null) { + packages.put(key, pkg); + } + }); + return packages; + } + + @Nullable + @Override + public AndroidPackage getPackageForUser(@NonNull String packageName, int userId) { + final FakeDeviceState.Package pkgState = mState.select(packageName, userId); + return pkgState == null ? null : pkgState.getPackageForUser(userId); + } + + @Override + public boolean isInstantApp(@NonNull String packageName, int userId) { + return false; } @Override @@ -371,14 +378,6 @@ class OverlayManagerServiceImplTestsBase { } @Override - public List<PackageInfo> getOverlayPackages(int userId) { - return mState.select(userId).stream() - .filter(p -> p.targetPackageName != null) - .map(p -> getPackageInfo(p.packageName, userId)) - .collect(Collectors.toList()); - } - - @Override public @NonNull String getConfigSignaturePackage() { return mConfigSignaturePackageName; } @@ -421,6 +420,9 @@ class OverlayManagerServiceImplTestsBase { static class FakeIdmapDaemon extends IdmapDaemon { private final FakeDeviceState mState; private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>(); + private final ArrayMap<String, FabricatedOverlayInfo> mFabricatedOverlays = + new ArrayMap<>(); + private int mFabricatedAssetSeq = 0; FakeIdmapDaemon(FakeDeviceState state) { this.mState = state; @@ -433,10 +435,10 @@ class OverlayManagerServiceImplTestsBase { } @Override - String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce, - int userId) { + String createIdmap(String targetPath, String overlayPath, String overlayName, + int policies, boolean enforce, int userId) { mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath), - getCrc(overlayPath), targetPath, policies, enforce)); + getCrc(overlayPath), targetPath, overlayName, policies, enforce)); return overlayPath; } @@ -446,8 +448,8 @@ class OverlayManagerServiceImplTestsBase { } @Override - boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce, - int userId) { + boolean verifyIdmap(String targetPath, String overlayPath, String overlayName, int policies, + boolean enforce, int userId) { final IdmapHeader idmap = mIdmapFiles.get(overlayPath); if (idmap == null) { return false; @@ -461,6 +463,29 @@ class OverlayManagerServiceImplTestsBase { return mIdmapFiles.containsKey(overlayPath); } + @Override + FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) { + final String path = Integer.toString(mFabricatedAssetSeq++); + final FabricatedOverlayInfo info = new FabricatedOverlayInfo(); + info.path = path; + info.overlayName = overlay.overlayName; + info.packageName = overlay.packageName; + info.targetPackageName = overlay.targetPackageName; + info.targetOverlayable = overlay.targetOverlayable; + mFabricatedOverlays.put(path, info); + return info; + } + + @Override + boolean deleteFabricatedOverlay(@NonNull String path) { + return mFabricatedOverlays.remove(path) != null; + } + + @Override + List<FabricatedOverlayInfo> getFabricatedOverlayInfos() { + return new ArrayList<>(mFabricatedOverlays.values()); + } + IdmapHeader getIdmap(String overlayPath) { return mIdmapFiles.get(overlayPath); } @@ -469,14 +494,16 @@ class OverlayManagerServiceImplTestsBase { private final int targetCrc; private final int overlayCrc; final String targetPath; + final String overlayName; final int policies; final boolean enforceOverlayable; - private IdmapHeader(int targetCrc, int overlayCrc, String targetPath, int policies, - boolean enforceOverlayable) { + private IdmapHeader(int targetCrc, int overlayCrc, String targetPath, + String overlayName, int policies, boolean enforceOverlayable) { this.targetCrc = targetCrc; this.overlayCrc = overlayCrc; this.targetPath = targetPath; + this.overlayName = overlayName; this.policies = policies; this.enforceOverlayable = enforceOverlayable; } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java index e3e77689f8e6..0a26f27bc848 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java @@ -19,17 +19,20 @@ package com.android.server.om; import static android.content.om.OverlayInfo.STATE_DISABLED; import static android.content.om.OverlayInfo.STATE_ENABLED; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.text.TextUtils; import android.util.TypedXmlPullParser; import android.util.Xml; +import androidx.annotation.NonNull; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; @@ -43,67 +46,32 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.IntStream; -import java.util.stream.Stream; + +import javax.annotation.Nullable; @RunWith(AndroidJUnit4.class) public class OverlayManagerSettingsTests { private OverlayManagerSettings mSettings; + private static int USER_0 = 0; + private static int USER_1 = 1; + + private static OverlayIdentifier OVERLAY_A = new OverlayIdentifier("com.test.overlay_a", + null /* overlayName */); + private static OverlayIdentifier OVERLAY_B = new OverlayIdentifier("com.test.overlay_b", + null /* overlayName */); + private static OverlayIdentifier OVERLAY_C = new OverlayIdentifier("com.test.overlay_c", + null /* overlayName */); + + private static final OverlayInfo OVERLAY_A_USER0 = createInfo(OVERLAY_A, USER_0); + private static final OverlayInfo OVERLAY_B_USER0 = createInfo(OVERLAY_B, USER_0); + private static final OverlayInfo OVERLAY_C_USER0 = createInfo(OVERLAY_C, USER_0); - private static final OverlayInfo OVERLAY_A0 = new OverlayInfo( - "com.test.overlay_a", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_a-1/base.apk", - STATE_DISABLED, - 0, - 0, - true); - - private static final OverlayInfo OVERLAY_B0 = new OverlayInfo( - "com.test.overlay_b", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_b-1/base.apk", - STATE_DISABLED, - 0, - 0, - true); - - private static final OverlayInfo OVERLAY_C0 = new OverlayInfo( - "com.test.overlay_c", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_c-1/base.apk", - STATE_DISABLED, - 0, - 0, - true); - - private static final OverlayInfo OVERLAY_A1 = new OverlayInfo( - "com.test.overlay_a", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_a-1/base.apk", - STATE_DISABLED, - 1, - 0, - true); - - private static final OverlayInfo OVERLAY_B1 = new OverlayInfo( - "com.test.overlay_b", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_b-1/base.apk", - STATE_DISABLED, - 1, - 0, - true); + private static final OverlayInfo OVERLAY_A_USER1 = createInfo(OVERLAY_A, USER_1); + private static final OverlayInfo OVERLAY_B_USER1 = createInfo(OVERLAY_B, USER_1); + + private static final String TARGET_PACKAGE = "com.test.target"; @Before public void setUp() throws Exception { @@ -114,124 +82,112 @@ public class OverlayManagerSettingsTests { @Test public void testSettingsInitiallyEmpty() throws Exception { - final int userId = 0; - Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(userId); + final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(0 /* userId */); assertEquals(0, map.size()); } @Test public void testBasicSetAndGet() throws Exception { - assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId); + assertDoesNotContain(mSettings, OVERLAY_A_USER0); - insert(OVERLAY_A0); - assertContains(mSettings, OVERLAY_A0); - OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId); - assertEquals(OVERLAY_A0, oi); + insertSetting(OVERLAY_A_USER0); + assertContains(mSettings, OVERLAY_A_USER0); + final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A, USER_0); + assertEquals(OVERLAY_A_USER0, oi); - assertTrue(mSettings.remove(OVERLAY_A0.packageName, OVERLAY_A0.userId)); - assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId); + assertTrue(mSettings.remove(OVERLAY_A, USER_0)); + assertDoesNotContain(mSettings, OVERLAY_A, USER_0); } @Test public void testGetUsers() throws Exception { - int[] users = mSettings.getUsers(); - assertEquals(0, users.length); - - insert(OVERLAY_A0); - users = mSettings.getUsers(); - assertEquals(1, users.length); - assertContains(users, OVERLAY_A0.userId); - - insert(OVERLAY_A1); - insert(OVERLAY_B1); - users = mSettings.getUsers(); - assertEquals(2, users.length); - assertContains(users, OVERLAY_A0.userId); - assertContains(users, OVERLAY_A1.userId); + assertArrayEquals(new int[]{}, mSettings.getUsers()); + + insertSetting(OVERLAY_A_USER0); + assertArrayEquals(new int[]{USER_0}, mSettings.getUsers()); + + insertSetting(OVERLAY_A_USER1); + insertSetting(OVERLAY_B_USER1); + assertArrayEquals(new int[]{USER_0, USER_1}, mSettings.getUsers()); } @Test public void testGetOverlaysForUser() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_A1); - insert(OVERLAY_B1); - - Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(OVERLAY_A0.userId); - assertEquals(1, map.keySet().size()); - assertTrue(map.keySet().contains(OVERLAY_A0.targetPackageName)); - - List<OverlayInfo> list = map.get(OVERLAY_A0.targetPackageName); - assertEquals(2, list.size()); - assertTrue(list.contains(OVERLAY_A0)); - assertTrue(list.contains(OVERLAY_B0)); - - // getOverlaysForUser should never return null - map = mSettings.getOverlaysForUser(-1); - assertNotNull(map); - assertEquals(0, map.size()); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_A_USER1); + insertSetting(OVERLAY_B_USER0); + + final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(USER_0); + assertEquals(Set.of(TARGET_PACKAGE), map.keySet()); + + // Two overlays in user 0 target the same package + final List<OverlayInfo> list = map.get(TARGET_PACKAGE); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0), list); + + // No users installed for user 3 + assertEquals(Map.<String, List<OverlayInfo>>of(), mSettings.getOverlaysForUser(3)); } @Test public void testRemoveUser() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_A1); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_A_USER1); - assertContains(mSettings, OVERLAY_A0); - assertContains(mSettings, OVERLAY_B0); - assertContains(mSettings, OVERLAY_A1); + assertContains(mSettings, OVERLAY_A_USER0); + assertContains(mSettings, OVERLAY_B_USER0); + assertContains(mSettings, OVERLAY_A_USER1); - mSettings.removeUser(OVERLAY_A0.userId); + mSettings.removeUser(USER_0); - assertDoesNotContain(mSettings, OVERLAY_A0); - assertDoesNotContain(mSettings, OVERLAY_B0); - assertContains(mSettings, OVERLAY_A1); + assertDoesNotContain(mSettings, OVERLAY_A_USER0); + assertDoesNotContain(mSettings, OVERLAY_B_USER0); + assertContains(mSettings, OVERLAY_A_USER1); } @Test public void testOrderOfNewlyAddedItems() throws Exception { // new items are appended to the list - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); - List<OverlayInfo> list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); // overlays keep their positions when updated - mSettings.setState(OVERLAY_B0.packageName, OVERLAY_B0.userId, STATE_ENABLED); - OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B0.packageName, OVERLAY_B0.userId); + mSettings.setState(OVERLAY_B, USER_0, STATE_ENABLED); + final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B, USER_0); + assertNotNull(oi); - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, oi, OVERLAY_C0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, oi, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); } @Test public void testSetPriority() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); - - List<OverlayInfo> list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); - - boolean changed = mSettings.setPriority(OVERLAY_B0.packageName, OVERLAY_C0.packageName, - OVERLAY_B0.userId); - assertTrue(changed); - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); - - changed = - mSettings.setPriority(OVERLAY_B0.packageName, "does.not.exist", OVERLAY_B0.userId); - assertFalse(changed); - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); - - OverlayInfo otherTarget = new OverlayInfo( + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); + + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); + + assertTrue(mSettings.setPriority(OVERLAY_B, OVERLAY_C, USER_0)); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); + + // Nothing happens if the parent package cannot be found + assertFalse(mSettings.setPriority(OVERLAY_B, new OverlayIdentifier("does.not.exist"), + USER_0)); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); + + // An overlay should not affect the priority of overlays targeting a different package + final OverlayInfo otherTarget = new OverlayInfo( "com.test.overlay_other", + null, "com.test.some.other.target", null, "some-category", @@ -239,45 +195,36 @@ public class OverlayManagerSettingsTests { STATE_DISABLED, 0, 0, - true); - insert(otherTarget); - changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName, - OVERLAY_A0.userId); - assertFalse(changed); + true, + false); + insertSetting(otherTarget); + assertFalse(mSettings.setPriority(OVERLAY_A, otherTarget.getOverlayIdentifier(), USER_0)); } @Test public void testSetLowestPriority() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); - - List<OverlayInfo> list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); - - boolean changed = mSettings.setLowestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId); - assertTrue(changed); - - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_B0, OVERLAY_A0, OVERLAY_C0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); + + assertTrue(mSettings.setLowestPriority(OVERLAY_B, USER_0)); + assertListsAreEqual(List.of(OVERLAY_B_USER0, OVERLAY_A_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); } @Test public void testSetHighestPriority() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); - - List<OverlayInfo> list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); - - boolean changed = mSettings.setHighestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId); - assertTrue(changed); - - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); + + assertTrue(mSettings.setHighestPriority(OVERLAY_B, USER_0)); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), + mSettings.getOverlaysForTarget(OVERLAY_A_USER0.targetPackageName, USER_0)); } // tests: persist and restore @@ -294,8 +241,8 @@ public class OverlayManagerSettingsTests { @Test public void testPersistDifferentOverlaysSameUser() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); ByteArrayOutputStream os = new ByteArrayOutputStream(); mSettings.persist(os); @@ -304,17 +251,17 @@ public class OverlayManagerSettingsTests { assertEquals(1, countXmlTags(xml, "overlays")); assertEquals(2, countXmlTags(xml, "item")); assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName", - OVERLAY_A0.packageName)); + OVERLAY_A.getPackageName())); assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName", - OVERLAY_B0.packageName)); + OVERLAY_B.getPackageName())); assertEquals(2, countXmlAttributesWhere(xml, "item", "userId", - Integer.toString(OVERLAY_A0.userId))); + Integer.toString(USER_0))); } @Test public void testPersistSameOverlayDifferentUsers() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_A1); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_A_USER1); ByteArrayOutputStream os = new ByteArrayOutputStream(); mSettings.persist(os); @@ -323,17 +270,17 @@ public class OverlayManagerSettingsTests { assertEquals(1, countXmlTags(xml, "overlays")); assertEquals(2, countXmlTags(xml, "item")); assertEquals(2, countXmlAttributesWhere(xml, "item", "packageName", - OVERLAY_A0.packageName)); + OVERLAY_A.getPackageName())); assertEquals(1, countXmlAttributesWhere(xml, "item", "userId", - Integer.toString(OVERLAY_A0.userId))); + Integer.toString(USER_0))); assertEquals(1, countXmlAttributesWhere(xml, "item", "userId", - Integer.toString(OVERLAY_A1.userId))); + Integer.toString(USER_1))); } @Test public void testPersistEnabled() throws Exception { - insert(OVERLAY_A0); - mSettings.setEnabled(OVERLAY_A0.packageName, OVERLAY_A0.userId, true); + insertSetting(OVERLAY_A_USER0); + mSettings.setEnabled(OVERLAY_A, USER_0, true); ByteArrayOutputStream os = new ByteArrayOutputStream(); mSettings.persist(os); @@ -351,7 +298,7 @@ public class OverlayManagerSettingsTests { ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); mSettings.restore(is); - assertDoesNotContain(mSettings, "com.test.overlay", 0); + assertDoesNotContain(mSettings, new OverlayIdentifier("com.test.overlay"), 0); } @Test @@ -361,6 +308,7 @@ public class OverlayManagerSettingsTests { "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n" + "<overlays version='" + version + "'>\n" + "<item packageName='com.test.overlay'\n" + + " overlayName='test'\n" + " userId='1234'\n" + " targetPackageName='com.test.target'\n" + " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n" @@ -373,20 +321,22 @@ public class OverlayManagerSettingsTests { ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); mSettings.restore(is); - OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234); + final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test"); + OverlayInfo oi = mSettings.getOverlayInfo(identifier, 1234); assertNotNull(oi); assertEquals("com.test.overlay", oi.packageName); + assertEquals("test", oi.overlayName); assertEquals("com.test.target", oi.targetPackageName); assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath); assertEquals(1234, oi.userId); assertEquals(STATE_DISABLED, oi.state); - assertFalse(mSettings.getEnabled("com.test.overlay", 1234)); + assertFalse(mSettings.getEnabled(identifier, 1234)); } @Test public void testPersistAndRestore() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B1); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER1); ByteArrayOutputStream os = new ByteArrayOutputStream(); mSettings.persist(os); @@ -394,11 +344,11 @@ public class OverlayManagerSettingsTests { OverlayManagerSettings newSettings = new OverlayManagerSettings(); newSettings.restore(is); - OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId); - assertEquals(OVERLAY_A0, a); + OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A, USER_0); + assertEquals(OVERLAY_A_USER0, a); - OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B1.packageName, OVERLAY_B1.userId); - assertEquals(OVERLAY_B1, b); + OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B, USER_1); + assertEquals(OVERLAY_B_USER1, b); } private int countXmlTags(InputStream in, String tagToLookFor) throws Exception { @@ -433,43 +383,53 @@ public class OverlayManagerSettingsTests { return count; } - private void insert(OverlayInfo oi) throws Exception { - mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath, - true, false,0, oi.category); - mSettings.setState(oi.packageName, oi.userId, oi.state); - mSettings.setEnabled(oi.packageName, oi.userId, false); + private void insertSetting(OverlayInfo oi) throws Exception { + mSettings.init(oi.getOverlayIdentifier(), oi.userId, oi.targetPackageName, null, + oi.baseCodePath, true, false,0, oi.category, oi.isFabricated); + mSettings.setState(oi.getOverlayIdentifier(), oi.userId, oi.state); + mSettings.setEnabled(oi.getOverlayIdentifier(), oi.userId, false); } private static void assertContains(final OverlayManagerSettings settings, final OverlayInfo oi) { - assertContains(settings, oi.packageName, oi.userId); - } - - private static void assertContains(final OverlayManagerSettings settings, - final String packageName, int userId) { try { - settings.getOverlayInfo(packageName, userId); + settings.getOverlayInfo(oi.getOverlayIdentifier(), oi.userId); } catch (OverlayManagerSettings.BadKeyException e) { - fail(String.format("settings does not contain packageName=%s userId=%d", - packageName, userId)); + fail(String.format("settings does not contain overlay=%s userId=%d", + oi.getOverlayIdentifier(), oi.userId)); } } private static void assertDoesNotContain(final OverlayManagerSettings settings, final OverlayInfo oi) { - assertDoesNotContain(settings, oi.packageName, oi.userId); + assertDoesNotContain(settings, oi.getOverlayIdentifier(), oi.userId); } private static void assertDoesNotContain(final OverlayManagerSettings settings, - final String packageName, int userId) { + final OverlayIdentifier overlay, int userId) { try { - settings.getOverlayInfo(packageName, userId); - fail(String.format("settings contains packageName=%s userId=%d", packageName, userId)); + settings.getOverlayInfo(overlay, userId); + fail(String.format("settings contains overlay=%s userId=%d", overlay, userId)); } catch (OverlayManagerSettings.BadKeyException e) { // do nothing: we expect to end up here } } + private static OverlayInfo createInfo(@NonNull OverlayIdentifier identifier, int userId) { + return new OverlayInfo( + identifier.getPackageName(), + identifier.getOverlayName(), + "com.test.target", + null, + "some-category", + "/data/app/" + identifier + "/base.apk", + STATE_DISABLED, + userId, + 0, + true, + false); + } + private static void assertContains(int[] haystack, int needle) { List<Integer> list = IntStream.of(haystack) .boxed() @@ -490,16 +450,11 @@ public class OverlayManagerSettingsTests { } } - private static void assertListsAreEqual(List<OverlayInfo> list, OverlayInfo... array) { - List<OverlayInfo> other = Stream.of(array) - .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); - assertListsAreEqual(list, other); - } - - private static void assertListsAreEqual(List<OverlayInfo> list, List<OverlayInfo> other) { - if (!list.equals(other)) { + private static void assertListsAreEqual( + @NonNull List<OverlayInfo> expected, @Nullable List<OverlayInfo> actual) { + if (!expected.equals(actual)) { fail(String.format("lists [%s] and [%s] differ", - TextUtils.join(",", list), TextUtils.join(",", other))); + TextUtils.join(",", expected), TextUtils.join(",", actual))); } } } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java index 22020ad45666..bc84e350a329 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java @@ -96,7 +96,7 @@ public class DexoptOptionsTests { int[] reasons = new int[] { PackageManagerService.REASON_FIRST_BOOT, - PackageManagerService.REASON_BOOT, + PackageManagerService.REASON_POST_BOOT, PackageManagerService.REASON_INSTALL, PackageManagerService.REASON_BACKGROUND_DEXOPT, PackageManagerService.REASON_AB_OTA, diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 21fd04ee3ae9..925b6f9601be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -29,6 +29,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -59,6 +60,10 @@ import android.app.ActivityTaskManager; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; +import android.graphics.ColorSpace; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; @@ -66,6 +71,8 @@ import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; import android.util.SparseBooleanArray; +import android.view.Surface; +import android.window.TaskSnapshot; import androidx.test.filters.MediumTest; @@ -446,12 +453,8 @@ public class RecentTasksTest extends WindowTestsBase { doReturn(false).when(child2).isOrganized(); mRecentTasks.add(root); - doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); - doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); - final List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, - true /* getTasksAllowed */, TEST_USER_0_ID, 0 /* callingUid */).getList(); - // Make sure only organized child will be appended. + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); final List<RecentTaskInfo> childrenTaskInfos = infos.get(0).childrenTaskInfos; assertEquals(childrenTaskInfos.size(), 1); assertEquals(childrenTaskInfos.get(0).taskId, child1.mTaskId); @@ -1051,9 +1054,6 @@ public class RecentTasksTest extends WindowTestsBase { @Test public void testTaskInfo_expectNoExtras() { - doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); - doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); - final Bundle data = new Bundle(); data.putInt("key", 100); final Task task1 = createTaskBuilder(".Task").build(); @@ -1063,8 +1063,7 @@ public class RecentTasksTest extends WindowTestsBase { .build(); mRecentTasks.add(r1.getTask()); - final List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, - true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList(); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); assertTrue(infos.size() == 1); for (int i = 0; i < infos.size(); i++) { final Bundle extras = infos.get(i).baseIntent.getExtras(); @@ -1072,6 +1071,60 @@ public class RecentTasksTest extends WindowTestsBase { } } + @Test + public void testLastSnapshotData_snapshotSaved() { + final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), new Point(80, 80)); + final Task task1 = createTaskBuilder(".Task").build(); + task1.onSnapshotChanged(snapshot); + + mRecentTasks.add(task1); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); + final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + infos.get(0).lastSnapshotData; + assertTrue(lastSnapshotData.taskSize.equals(100, 100)); + assertTrue(lastSnapshotData.bufferSize.equals(80, 80)); + } + + @Test + public void testLastSnapshotData_noBuffer() { + final Task task1 = createTaskBuilder(".Task").build(); + final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), null); + task1.onSnapshotChanged(snapshot); + + mRecentTasks.add(task1); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); + final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + infos.get(0).lastSnapshotData; + assertTrue(lastSnapshotData.taskSize.equals(100, 100)); + assertNull(lastSnapshotData.bufferSize); + } + + @Test + public void testLastSnapshotData_notSet() { + final Task task1 = createTaskBuilder(".Task").build(); + + mRecentTasks.add(task1); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); + final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + infos.get(0).lastSnapshotData; + assertNull(lastSnapshotData.taskSize); + assertNull(lastSnapshotData.bufferSize); + } + + private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) { + HardwareBuffer buffer = null; + if (bufferSize != null) { + buffer = mock(HardwareBuffer.class); + doReturn(bufferSize.x).when(buffer).getWidth(); + doReturn(bufferSize.y).when(buffer).getHeight(); + } + return new TaskSnapshot(1, new ComponentName("", ""), buffer, + ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, + Surface.ROTATION_0, taskSize, new Rect() /* insets */, false /* isLowResolution */, + true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */, + false /* isTranslucent */, false /* hasImeSurface */); + } + /** * Ensures that the raw recent tasks list is in the provided order. Note that the expected tasks * should be ordered from least to most recent. @@ -1084,15 +1137,19 @@ public class RecentTasksTest extends WindowTestsBase { } } + private List<RecentTaskInfo> getRecentTasks(int flags) { + doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); + doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); + return mRecentTasks.getRecentTasks(MAX_VALUE, flags, true /* getTasksAllowed */, + TEST_USER_0_ID, 0 /* callingUid */).getList(); + } + /** * Ensures that the recent tasks list is in the provided order. Note that the expected tasks * should be ordered from least to most recent. */ private void assertGetRecentTasksOrder(int getRecentTaskFlags, Task... expectedTasks) { - doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); - doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); - List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, getRecentTaskFlags, - true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList(); + List<RecentTaskInfo> infos = getRecentTasks(getRecentTaskFlags); assertTrue(expectedTasks.length == infos.size()); for (int i = 0; i < infos.size(); i++) { assertTrue(expectedTasks[i].mTaskId == infos.get(i).taskId); diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 3b06fd3d4ea1..170ed3eff614 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -2024,7 +2024,7 @@ public abstract class ConnectionService extends Service { boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean( TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false); boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean( - PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false); + PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true); Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, " + " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming, diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt index b2bcfeb9019d..ad5bbf220d57 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt @@ -54,12 +54,26 @@ class CaptivePortalDataTest { } .build() + private val dataFromPasspoint = CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setCaptive(true) + .apply { + if (SdkLevel.isAtLeastS()) { + setVenueFriendlyName("venue friendly name") + } + } + .build() + private fun makeBuilder() = CaptivePortalData.Builder(data) @Test fun testParcelUnparcel() { - val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7 + val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7 assertParcelSane(data, fieldCount) + assertParcelSane(dataFromPasspoint, fieldCount) assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) @@ -83,6 +97,27 @@ class CaptivePortalDataTest { assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } } + + assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build()) + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } } @Test @@ -130,6 +165,22 @@ class CaptivePortalDataTest { assertEquals("venue friendly name", data.venueFriendlyName) } + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testGetVenueInfoUrlSource() { + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + data.venueInfoUrlSource) + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, + dataFromPasspoint.venueInfoUrlSource) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testGetUserPortalUrlSource() { + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + data.userPortalUrlSource) + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, + dataFromPasspoint.userPortalUrlSource) + } + private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) = CaptivePortalData.Builder(this).apply { mutator(this) }.build() diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index e1fd8f0d7d1d..e639a369eca2 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -362,8 +362,15 @@ public class ConnectivityServiceTest { private static final String INTERFACE_NAME = "interface"; - private static final String TEST_VENUE_URL_NA = "https://android.com/"; + private static final String TEST_VENUE_URL_NA_PASSPOINT = "https://android.com/"; + private static final String TEST_VENUE_URL_NA_OTHER = "https://example.com/"; + private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT = + "https://android.com/terms/"; + private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER = + "https://example.com/terms/"; private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/"; + private static final String TEST_USER_PORTAL_API_URL_CAPPORT = + "https://android.com/user/api/capport/"; private static final String TEST_FRIENDLY_NAME = "Network friendly name"; private static final String TEST_REDIRECT_URL = "http://example.com/firstPath"; @@ -3289,39 +3296,68 @@ public class ConnectivityServiceTest { } private class CaptivePortalTestData { - CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData, - CaptivePortalData expectedMergedData) { - mNaData = naData; + CaptivePortalTestData(CaptivePortalData naPasspointData, CaptivePortalData capportData, + CaptivePortalData naOtherData, CaptivePortalData expectedMergedPasspointData, + CaptivePortalData expectedMergedOtherData) { + mNaPasspointData = naPasspointData; mCapportData = capportData; - mExpectedMergedData = expectedMergedData; + mNaOtherData = naOtherData; + mExpectedMergedPasspointData = expectedMergedPasspointData; + mExpectedMergedOtherData = expectedMergedOtherData; } - public final CaptivePortalData mNaData; + public final CaptivePortalData mNaPasspointData; public final CaptivePortalData mCapportData; - public final CaptivePortalData mExpectedMergedData; + public final CaptivePortalData mNaOtherData; + public final CaptivePortalData mExpectedMergedPasspointData; + public final CaptivePortalData mExpectedMergedOtherData; + } private CaptivePortalTestData setupCaptivePortalData() { final CaptivePortalData capportData = new CaptivePortalData.Builder() .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) .setExpiryTime(1000000L) .setBytesRemaining(12345L) .build(); - final CaptivePortalData naData = new CaptivePortalData.Builder() + final CaptivePortalData naPasspointData = new CaptivePortalData.Builder() .setBytesRemaining(80802L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder() + final CaptivePortalData naOtherData = new CaptivePortalData.Builder() + .setBytesRemaining(80802L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_OTHER), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + final CaptivePortalData expectedMergedPasspointData = new CaptivePortalData.Builder() .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) .setBytesRemaining(12345L) .setExpiryTime(1000000L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - return new CaptivePortalTestData(naData, capportData, expectedMergedData); + final CaptivePortalData expectedMergedOtherData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setBytesRemaining(12345L) + .setExpiryTime(1000000L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + return new CaptivePortalTestData(naPasspointData, capportData, naOtherData, + expectedMergedPasspointData, expectedMergedOtherData); } @Test @@ -3335,15 +3371,26 @@ public class ConnectivityServiceTest { captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); - // Venue URL and friendly name from Network agent, confirm that API data gets precedence - // on the bytes remaining. + // Venue URL, T&C URL and friendly name from Network agent with Passpoint source, confirm + // that API data gets precedence on the bytes remaining. final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); mWiFiNetworkAgent.sendLinkProperties(linkProperties); // Make sure that the capport data is merged captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mExpectedMergedPasspointData + .equals(lp.getCaptivePortalData())); + + // Now send this information from non-Passpoint source, confirm that Capport data takes + // precedence + linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the capport data is merged + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedOtherData + .equals(lp.getCaptivePortalData())); // Create a new LP with no Network agent capport data final LinkProperties newLps = new LinkProperties(); @@ -3360,12 +3407,12 @@ public class ConnectivityServiceTest { captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> lp.getCaptivePortalData() == null); - newLps.setCaptivePortalData(captivePortalTestData.mNaData); + newLps.setCaptivePortalData(captivePortalTestData.mNaPasspointData); mWiFiNetworkAgent.sendLinkProperties(newLps); // Make sure that only the network agent capport data is available captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); } @Test @@ -3376,12 +3423,12 @@ public class ConnectivityServiceTest { // Venue URL and friendly name from Network agent, confirm that API data gets precedence // on the bytes remaining. final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); mWiFiNetworkAgent.sendLinkProperties(linkProperties); // Make sure that the data is saved correctly captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); // Expected merged data: Network agent data is preferred, and values that are not used by // it are merged from capport data @@ -3389,7 +3436,8 @@ public class ConnectivityServiceTest { // Make sure that the Capport data is merged correctly captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mExpectedMergedPasspointData.equals( + lp.getCaptivePortalData())); // Now set the naData to null linkProperties.setCaptivePortalData(null); @@ -3400,6 +3448,32 @@ public class ConnectivityServiceTest { lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); } + @Test + public void testMergeCaptivePortalDataFromNetworkAgentOtherSourceFirstThenCapport() + throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the data is saved correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaOtherData.equals(lp.getCaptivePortalData())); + + // Expected merged data: Network agent data is preferred, and values that are not used by + // it are merged from capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + // Make sure that the Capport data is merged correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedOtherData.equals( + lp.getCaptivePortalData())); + } + private NetworkRequest.Builder newWifiRequestBuilder() { return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); } diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index cd4cfcf18804..46c408199b1a 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -67,6 +67,8 @@ import java.util.List; @SmallTest public class NetworkNotificationManagerTest { + private static final String TEST_SSID = "Test SSID"; + private static final String TEST_EXTRA_INFO = "extra"; static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities(); static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities(); static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities(); @@ -76,6 +78,7 @@ public class NetworkNotificationManagerTest { WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + WIFI_CAPABILITIES.setSSID(TEST_SSID); // Set the underyling network to wifi. VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); @@ -129,7 +132,7 @@ public class NetworkNotificationManagerTest { when(mCtx.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE))) .thenReturn(mNotificationManager); - when(mNetworkInfo.getExtraInfo()).thenReturn("extra"); + when(mNetworkInfo.getExtraInfo()).thenReturn(TEST_EXTRA_INFO); when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B); when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); @@ -143,11 +146,11 @@ public class NetworkNotificationManagerTest { .notify(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any()); final int transportType = NetworkNotificationManager.approximateTransportType(nai); if (transportType == NetworkCapabilities.TRANSPORT_WIFI) { - verify(mResources, times(1)).getString(title, eq(any())); + verify(mResources, times(1)).getString(eq(title), eq(TEST_EXTRA_INFO)); } else { verify(mResources, times(1)).getString(title); } - verify(mResources, times(1)).getString(R.string.private_dns_broken_detailed); + verify(mResources, times(1)).getString(eq(R.string.private_dns_broken_detailed)); } @Test |